feat: migrate templates and documents to envelope model

This commit is contained in:
David Nguyen
2025-09-11 18:23:38 +10:00
parent eec2307634
commit bf89bc781b
234 changed files with 8677 additions and 6054 deletions

View File

@ -1,5 +1,5 @@
import { adminSuperDeleteDocument } from '@documenso/lib/server-only/admin/admin-super-delete-document';
import { sendDeleteEmail } from '@documenso/lib/server-only/document/send-delete-email';
import { superDeleteDocument } from '@documenso/lib/server-only/document/super-delete-document';
import { adminProcedure } from '../trpc';
import {
@ -19,10 +19,10 @@ export const deleteDocumentRoute = adminProcedure
},
});
await sendDeleteEmail({ documentId: id, reason });
await sendDeleteEmail({ envelopeId: id, reason });
await superDeleteDocument({
id,
await adminSuperDeleteDocument({
envelopeId: id,
requestMetadata: ctx.metadata.requestMetadata,
});
});

View File

@ -1,7 +1,7 @@
import { z } from 'zod';
export const ZDeleteDocumentRequestSchema = z.object({
id: z.number().min(1),
id: z.string(),
reason: z.string(),
});

View File

@ -1,4 +1,5 @@
import { findDocuments } from '@documenso/lib/server-only/admin/get-all-documents';
import { adminFindDocuments } from '@documenso/lib/server-only/admin/admin-find-documents';
import { mapEnvelopesToDocumentMany } from '@documenso/lib/utils/document';
import { adminProcedure } from '../trpc';
import { ZFindDocumentsRequestSchema, ZFindDocumentsResponseSchema } from './find-documents.types';
@ -9,5 +10,10 @@ export const findDocumentsRoute = adminProcedure
.query(async ({ input }) => {
const { query, page, perPage } = input;
return await findDocuments({ query, page, perPage });
const result = await adminFindDocuments({ query, page, perPage });
return {
...result,
data: result.data.map(mapEnvelopesToDocumentMany),
};
});

View File

@ -1,4 +1,6 @@
import { getEntireDocument } from '@documenso/lib/server-only/admin/get-entire-document';
import { EnvelopeType } from '@prisma/client';
import { unsafeGetEntireEnvelope } from '@documenso/lib/server-only/admin/get-entire-document';
import { sealDocument } from '@documenso/lib/server-only/document/seal-document';
import { isDocumentCompleted } from '@documenso/lib/utils/document';
@ -20,9 +22,21 @@ export const resealDocumentRoute = adminProcedure
},
});
const document = await getEntireDocument({ id });
const envelope = await unsafeGetEntireEnvelope({
id: {
type: 'envelopeId',
id,
},
type: EnvelopeType.DOCUMENT,
});
const isResealing = isDocumentCompleted(document.status);
const isResealing = isDocumentCompleted(envelope.status);
await sealDocument({ documentId: id, isResealing });
await sealDocument({
id: {
type: 'envelopeId',
id,
},
isResealing,
});
});

View File

@ -1,7 +1,7 @@
import { z } from 'zod';
export const ZResealDocumentRequestSchema = z.object({
id: z.number().min(1),
id: z.string(),
});
export const ZResealDocumentResponseSchema = z.void();

View File

@ -1,10 +1,12 @@
import { DocumentDataType } from '@prisma/client';
import { DocumentDataType, EnvelopeType } from '@prisma/client';
import { getServerLimits } from '@documenso/ee/server-only/limits/server';
import { AppError, AppErrorCode } from '@documenso/lib/errors/app-error';
import { createDocumentData } from '@documenso/lib/server-only/document-data/create-document-data';
import { createDocumentV2 } from '@documenso/lib/server-only/document/create-document-v2';
import { createEnvelope } from '@documenso/lib/server-only/envelope/create-envelope';
import { getPresignPostUrl } from '@documenso/lib/universal/upload/server-actions';
import { mapSecondaryIdToDocumentId } from '@documenso/lib/utils/envelope';
import { prisma } from '@documenso/prisma';
import { authenticatedProcedure } from '../trpc';
import {
@ -55,12 +57,12 @@ export const createDocumentTemporaryRoute = authenticatedProcedure
type: DocumentDataType.S3_PATH,
});
const createdDocument = await createDocumentV2({
const createdEnvelope = await createEnvelope({
userId: ctx.user.id,
teamId,
documentDataId: documentData.id,
normalizePdf: false, // Not normalizing because of presigned URL.
data: {
type: EnvelopeType.DOCUMENT,
title,
externalId,
visibility,
@ -68,14 +70,44 @@ export const createDocumentTemporaryRoute = authenticatedProcedure
globalActionAuth,
recipients,
folderId,
envelopeItems: [
{
documentDataId: documentData.id,
},
],
},
meta,
requestMetadata: ctx.metadata,
});
const envelopeItems = await prisma.envelopeItem.findMany({
where: {
envelopeId: createdEnvelope.id,
},
include: {
documentData: true,
},
});
const legacyDocumentId = mapSecondaryIdToDocumentId(createdEnvelope.secondaryId);
const firstDocumentData = envelopeItems[0].documentData;
if (!firstDocumentData) {
throw new Error('Document data not found');
}
return {
document: createdDocument,
folder: createdDocument.folder, // Todo: Remove this prior to api-v2 release.
document: {
...createdEnvelope,
documentData: firstDocumentData,
id: legacyDocumentId,
fields: createdEnvelope.fields.map((field) => ({
...field,
documentId: legacyDocumentId,
})),
},
folder: createdEnvelope.folder, // Todo: Remove this prior to api-v2 release.
uploadUrl: url,
};
});

View File

@ -1,6 +1,9 @@
import { EnvelopeType } from '@prisma/client';
import { getServerLimits } from '@documenso/ee/server-only/limits/server';
import { AppError, AppErrorCode } from '@documenso/lib/errors/app-error';
import { createDocument } from '@documenso/lib/server-only/document/create-document';
import { createEnvelope } from '@documenso/lib/server-only/envelope/create-envelope';
import { mapSecondaryIdToDocumentId } from '@documenso/lib/utils/envelope';
import { authenticatedProcedure } from '../trpc';
import {
@ -9,7 +12,7 @@ import {
} from './create-document.types';
export const createDocumentRoute = authenticatedProcedure
.input(ZCreateDocumentRequestSchema)
.input(ZCreateDocumentRequestSchema) // Note: Before releasing this to public, update the response schema to be correct.
.output(ZCreateDocumentResponseSchema)
.mutation(async ({ input, ctx }) => {
const { user, teamId } = ctx;
@ -30,18 +33,25 @@ export const createDocumentRoute = authenticatedProcedure
});
}
const document = await createDocument({
const document = await createEnvelope({
userId: user.id,
teamId,
title,
documentDataId,
data: {
type: EnvelopeType.DOCUMENT,
title,
userTimezone: timezone,
folderId,
envelopeItems: [
{
documentDataId,
},
],
},
normalizePdf: true,
userTimezone: timezone,
requestMetadata: ctx.metadata,
folderId,
});
return {
id: document.id,
legacyDocumentId: mapSecondaryIdToDocumentId(document.secondaryId),
};
});

View File

@ -20,7 +20,7 @@ export const ZCreateDocumentRequestSchema = z.object({
});
export const ZCreateDocumentResponseSchema = z.object({
id: z.number(),
legacyDocumentId: z.number(),
});
export type TCreateDocumentRequest = z.infer<typeof ZCreateDocumentRequestSchema>;

View File

@ -1,5 +1,6 @@
import { upsertDocumentMeta } from '@documenso/lib/server-only/document-meta/upsert-document-meta';
import { updateDocumentMeta } from '@documenso/lib/server-only/document-meta/upsert-document-meta';
import { sendDocument } from '@documenso/lib/server-only/document/send-document';
import { mapEnvelopeToDocumentLite } from '@documenso/lib/utils/document';
import { authenticatedProcedure } from '../trpc';
import {
@ -23,10 +24,13 @@ export const distributeDocumentRoute = authenticatedProcedure
});
if (Object.values(meta).length > 0) {
await upsertDocumentMeta({
await updateDocumentMeta({
userId: ctx.user.id,
teamId,
documentId,
id: {
type: 'documentId',
id: documentId,
},
subject: meta.subject,
message: meta.message,
dateFormat: meta.dateFormat,
@ -41,10 +45,15 @@ export const distributeDocumentRoute = authenticatedProcedure
});
}
return await sendDocument({
const envelope = await sendDocument({
userId: ctx.user.id,
documentId,
id: {
type: 'documentId',
id: documentId,
},
teamId,
requestMetadata: ctx.metadata,
});
return mapEnvelopeToDocumentLite(envelope);
});

View File

@ -1,9 +1,11 @@
import { EnvelopeType } from '@prisma/client';
import { DateTime } from 'luxon';
import { NEXT_PUBLIC_WEBAPP_URL } from '@documenso/lib/constants/app';
import { AppError, AppErrorCode } from '@documenso/lib/errors/app-error';
import { encryptSecondaryData } from '@documenso/lib/server-only/crypto/encrypt';
import { getDocumentById } from '@documenso/lib/server-only/document/get-document-by-id';
import { getEnvelopeById } from '@documenso/lib/server-only/envelope/get-envelope-by-id';
import { mapSecondaryIdToDocumentId } from '@documenso/lib/utils/envelope';
import { authenticatedProcedure } from '../trpc';
import {
@ -24,20 +26,24 @@ export const downloadDocumentAuditLogsRoute = authenticatedProcedure
},
});
const document = await getDocumentById({
documentId,
const envelope = await getEnvelopeById({
id: {
type: 'documentId',
id: documentId,
},
type: EnvelopeType.DOCUMENT,
userId: ctx.user.id,
teamId,
}).catch(() => null);
if (!document || (teamId && document.teamId !== teamId)) {
if (!envelope) {
throw new AppError(AppErrorCode.UNAUTHORIZED, {
message: 'You do not have access to this document.',
});
}
const encrypted = encryptSecondaryData({
data: document.id.toString(),
data: mapSecondaryIdToDocumentId(envelope.secondaryId).toString(),
expiresAt: DateTime.now().plus({ minutes: 5 }).toJSDate().valueOf(),
});

View File

@ -1,10 +1,12 @@
import { EnvelopeType } from '@prisma/client';
import { DateTime } from 'luxon';
import { NEXT_PUBLIC_WEBAPP_URL } from '@documenso/lib/constants/app';
import { AppError } from '@documenso/lib/errors/app-error';
import { encryptSecondaryData } from '@documenso/lib/server-only/crypto/encrypt';
import { getDocumentById } from '@documenso/lib/server-only/document/get-document-by-id';
import { getEnvelopeById } from '@documenso/lib/server-only/envelope/get-envelope-by-id';
import { isDocumentCompleted } from '@documenso/lib/utils/document';
import { mapSecondaryIdToDocumentId } from '@documenso/lib/utils/envelope';
import { authenticatedProcedure } from '../trpc';
import {
@ -25,18 +27,22 @@ export const downloadDocumentCertificateRoute = authenticatedProcedure
},
});
const document = await getDocumentById({
documentId,
const envelope = await getEnvelopeById({
id: {
type: 'documentId',
id: documentId,
},
type: EnvelopeType.DOCUMENT,
userId: ctx.user.id,
teamId,
});
if (!isDocumentCompleted(document.status)) {
if (!isDocumentCompleted(envelope.status)) {
throw new AppError('DOCUMENT_NOT_COMPLETE');
}
const encrypted = encryptSecondaryData({
data: document.id.toString(),
data: mapSecondaryIdToDocumentId(envelope.secondaryId).toString(),
expiresAt: DateTime.now().plus({ minutes: 5 }).toJSDate().valueOf(),
});

View File

@ -1,7 +1,8 @@
import { DocumentDataType } from '@prisma/client';
import type { DocumentData } from '@prisma/client';
import { DocumentDataType, EnvelopeType } from '@prisma/client';
import { AppError, AppErrorCode } from '@documenso/lib/errors/app-error';
import { getDocumentById } from '@documenso/lib/server-only/document/get-document-by-id';
import { getEnvelopeById } from '@documenso/lib/server-only/envelope/get-envelope-by-id';
import { getPresignGetUrl } from '@documenso/lib/universal/upload/server-actions';
import { isDocumentCompleted } from '@documenso/lib/utils/document';
@ -27,45 +28,51 @@ export const downloadDocumentRoute = authenticatedProcedure
},
});
const envelope = await getEnvelopeById({
id: {
type: 'documentId',
id: documentId,
},
type: EnvelopeType.DOCUMENT,
userId: user.id,
teamId,
});
// This error is done AFTER the get envelope so we can test access controls without S3.
if (process.env.NEXT_PUBLIC_UPLOAD_TRANSPORT !== 's3') {
throw new AppError(AppErrorCode.INVALID_REQUEST, {
message: 'Document downloads are only available when S3 storage is configured.',
});
}
const document = await getDocumentById({
documentId,
userId: user.id,
teamId,
});
const documentData: DocumentData | undefined = envelope.envelopeItems[0]?.documentData;
if (!document.documentData) {
throw new AppError(AppErrorCode.NOT_FOUND, {
message: 'Document data not found',
if (envelope.envelopeItems.length !== 1 || !documentData) {
throw new AppError(AppErrorCode.INVALID_REQUEST, {
message:
'This endpoint only supports documents with a single item. Use envelopes API instead.',
});
}
if (document.documentData.type !== DocumentDataType.S3_PATH) {
if (documentData.type !== DocumentDataType.S3_PATH) {
throw new AppError(AppErrorCode.INVALID_REQUEST, {
message: 'Document is not stored in S3 and cannot be downloaded via URL.',
});
}
if (version === 'signed' && !isDocumentCompleted(document.status)) {
if (version === 'signed' && !isDocumentCompleted(envelope.status)) {
throw new AppError(AppErrorCode.INVALID_REQUEST, {
message: 'Document is not completed yet.',
});
}
try {
const documentData =
version === 'original'
? document.documentData.initialData || document.documentData.data
: document.documentData.data;
const data =
version === 'original' ? documentData.initialData || documentData.data : documentData.data;
const { url } = await getPresignGetUrl(documentData);
const { url } = await getPresignGetUrl(data);
const baseTitle = document.title.replace(/\.pdf$/, '');
const baseTitle = envelope.title.replace(/\.pdf$/, '');
const suffix = version === 'signed' ? '_signed.pdf' : '.pdf';
const filename = `${baseTitle}${suffix}`;

View File

@ -22,8 +22,11 @@ export const duplicateDocumentRoute = authenticatedProcedure
});
return await duplicateDocument({
id: {
type: 'documentId',
id: documentId,
},
userId: user.id,
teamId,
documentId,
});
});

View File

@ -2,6 +2,7 @@ import { findDocuments } from '@documenso/lib/server-only/document/find-document
import type { GetStatsInput } from '@documenso/lib/server-only/document/get-stats';
import { getStats } from '@documenso/lib/server-only/document/get-stats';
import { getTeamById } from '@documenso/lib/server-only/team/get-team';
import { mapEnvelopesToDocumentMany } from '@documenso/lib/utils/document';
import { authenticatedProcedure } from '../trpc';
import {
@ -69,6 +70,7 @@ export const findDocumentsInternalRoute = authenticatedProcedure
return {
...documents,
data: documents.data.map((envelope) => mapEnvelopesToDocumentMany(envelope)),
stats,
};
});

View File

@ -1,4 +1,5 @@
import { findDocuments } from '@documenso/lib/server-only/document/find-documents';
import { mapEnvelopesToDocumentMany } from '@documenso/lib/utils/document';
import { authenticatedProcedure } from '../trpc';
import {
@ -39,5 +40,8 @@ export const findDocumentsRoute = authenticatedProcedure
orderBy: orderByColumn ? { column: orderByColumn, direction: orderByDirection } : undefined,
});
return documents;
return {
...documents,
data: documents.data.map((envelope) => mapEnvelopesToDocumentMany(envelope)),
};
});

View File

@ -1,7 +1,8 @@
import type { Document, Prisma } from '@prisma/client';
import { DocumentStatus, RecipientRole } from '@prisma/client';
import type { Envelope, Prisma } from '@prisma/client';
import { DocumentStatus, EnvelopeType, RecipientRole } from '@prisma/client';
import type { FindResultResponse } from '@documenso/lib/types/search-params';
import { mapEnvelopesToDocumentMany } from '@documenso/lib/utils/document';
import { maskRecipientTokensForDocument } from '@documenso/lib/utils/mask-recipient-tokens-for-document';
import { prisma } from '@documenso/prisma';
@ -16,11 +17,16 @@ export const findInboxRoute = authenticatedProcedure
const userId = ctx.user.id;
return await findInbox({
const envelopes = await findInbox({
userId,
page,
perPage,
});
return {
...envelopes,
data: envelopes.data.map(mapEnvelopesToDocumentMany),
};
});
export type FindInboxOptions = {
@ -28,7 +34,7 @@ export type FindInboxOptions = {
page?: number;
perPage?: number;
orderBy?: {
column: keyof Omit<Document, 'document'>;
column: keyof Omit<Envelope, 'envelope'>;
direction: 'asc' | 'desc';
};
};
@ -38,12 +44,17 @@ export const findInbox = async ({ userId, page = 1, perPage = 10, orderBy }: Fin
where: {
id: userId,
},
select: {
id: true,
email: true,
},
});
const orderByColumn = orderBy?.column ?? 'createdAt';
const orderByDirection = orderBy?.direction ?? 'desc';
const whereClause: Prisma.DocumentWhereInput = {
const whereClause: Prisma.EnvelopeWhereInput = {
type: EnvelopeType.DOCUMENT,
status: {
not: DocumentStatus.DRAFT,
},
@ -59,7 +70,7 @@ export const findInbox = async ({ userId, page = 1, perPage = 10, orderBy }: Fin
};
const [data, count] = await Promise.all([
prisma.document.findMany({
prisma.envelope.findMany({
where: whereClause,
skip: Math.max(page - 1, 0) * perPage,
take: perPage,
@ -83,7 +94,7 @@ export const findInbox = async ({ userId, page = 1, perPage = 10, orderBy }: Fin
},
},
}),
prisma.document.count({
prisma.envelope.count({
where: whereClause,
}),
]);

View File

@ -1,3 +1,5 @@
import { EnvelopeType } from '@prisma/client';
import { AppError, AppErrorCode } from '@documenso/lib/errors/app-error';
import { prisma } from '@documenso/prisma';
@ -13,8 +15,9 @@ export const getDocumentByTokenRoute = authenticatedProcedure
.query(async ({ input, ctx }) => {
const { token } = input;
const document = await prisma.document.findFirst({
const envelope = await prisma.envelope.findFirst({
where: {
type: EnvelopeType.DOCUMENT,
recipients: {
some: {
token,
@ -23,21 +26,34 @@ export const getDocumentByTokenRoute = authenticatedProcedure
},
},
include: {
documentData: true,
envelopeItems: {
include: {
documentData: true,
},
},
},
});
if (!document) {
// Todo: Envelopes
const firstDocumentData = envelope?.envelopeItems[0].documentData;
if (!envelope || !firstDocumentData) {
throw new AppError(AppErrorCode.NOT_FOUND, {
message: 'Document not found',
});
}
if (envelope.envelopeItems.length !== 1) {
throw new AppError(AppErrorCode.INVALID_REQUEST, {
message: 'This endpoint does not support multiple items',
});
}
ctx.logger.info({
documentId: document.id,
documentId: envelope.id,
});
return {
documentData: document.documentData,
documentData: firstDocumentData,
};
});

View File

@ -24,6 +24,9 @@ export const getDocumentRoute = authenticatedProcedure
return await getDocumentWithDetailsById({
userId: user.id,
teamId,
documentId,
id: {
type: 'documentId',
id: documentId,
},
});
});

View File

@ -1,4 +1,4 @@
import { DocumentStatus, RecipientRole } from '@prisma/client';
import { DocumentStatus, EnvelopeType, RecipientRole } from '@prisma/client';
import { prisma } from '@documenso/prisma';
@ -20,7 +20,8 @@ export const getInboxCountRoute = authenticatedProcedure
role: {
not: RecipientRole.CC,
},
document: {
envelope: {
type: EnvelopeType.DOCUMENT,
status: {
not: DocumentStatus.DRAFT,
},

View File

@ -17,6 +17,7 @@ import { getDocumentByTokenRoute } from './get-document-by-token';
import { getInboxCountRoute } from './get-inbox-count';
import { redistributeDocumentRoute } from './redistribute-document';
import { searchDocumentRoute } from './search-document';
import { shareDocumentRoute } from './share-document';
import { updateDocumentRoute } from './update-document';
export const documentRouter = router({
@ -30,6 +31,7 @@ export const documentRouter = router({
distribute: distributeDocumentRoute,
redistribute: redistributeDocumentRoute,
search: searchDocumentRoute,
share: shareDocumentRoute,
// Temporary v2 beta routes to be removed once V2 is fully released.
download: downloadDocumentRoute,

View File

@ -0,0 +1,28 @@
import { createOrGetShareLink } from '@documenso/lib/server-only/share/create-or-get-share-link';
import { procedure } from '../trpc';
import { ZShareDocumentRequestSchema, ZShareDocumentResponseSchema } from './share-document.types';
// Note: This is an unauthenticated route.
export const shareDocumentRoute = procedure
.input(ZShareDocumentRequestSchema)
.output(ZShareDocumentResponseSchema)
.mutation(async ({ input, ctx }) => {
const { documentId, token } = input;
ctx.logger.info({
input: {
documentId,
},
});
if (token) {
return await createOrGetShareLink({ documentId, token });
}
if (!ctx.user?.id) {
throw new Error('You must either provide a token or be logged in to create a sharing link.');
}
return await createOrGetShareLink({ documentId, userId: ctx.user.id });
});

View File

@ -0,0 +1,16 @@
import { z } from 'zod';
import DocumentShareLinkSchema from '@documenso/prisma/generated/zod/modelSchema/DocumentShareLinkSchema';
export const ZShareDocumentRequestSchema = z.object({
documentId: z.number(),
token: z.string().optional(),
});
export const ZShareDocumentResponseSchema = DocumentShareLinkSchema.pick({
slug: true,
email: true,
});
export type TShareDocumentRequest = z.infer<typeof ZShareDocumentRequestSchema>;
export type TShareDocumentResponse = z.infer<typeof ZShareDocumentResponseSchema>;

View File

@ -1,18 +1,19 @@
import { upsertDocumentMeta } from '@documenso/lib/server-only/document-meta/upsert-document-meta';
import { updateDocumentMeta } from '@documenso/lib/server-only/document-meta/upsert-document-meta';
import { updateDocument } from '@documenso/lib/server-only/document/update-document';
import { mapSecondaryIdToDocumentId } from '@documenso/lib/utils/envelope';
import { authenticatedProcedure } from '../trpc';
import {
ZUpdateDocumentRequestSchema,
ZUpdateDocumentResponseSchema,
} from './update-document.types';
import { updateDocumentMeta } from './update-document.types';
import { updateDocumentMeta as updateDocumentTrpcMeta } from './update-document.types';
/**
* Public route.
*/
export const updateDocumentRoute = authenticatedProcedure
.meta(updateDocumentMeta)
.meta(updateDocumentTrpcMeta)
.input(ZUpdateDocumentRequestSchema)
.output(ZUpdateDocumentResponseSchema)
.mutation(async ({ input, ctx }) => {
@ -28,10 +29,13 @@ export const updateDocumentRoute = authenticatedProcedure
const userId = ctx.user.id;
if (Object.values(meta).length > 0) {
await upsertDocumentMeta({
await updateDocumentMeta({
userId: ctx.user.id,
teamId,
documentId,
id: {
type: 'documentId',
id: documentId,
},
subject: meta.subject,
message: meta.message,
timezone: meta.timezone,
@ -51,11 +55,18 @@ export const updateDocumentRoute = authenticatedProcedure
});
}
return await updateDocument({
const envelope = await updateDocument({
userId,
teamId,
documentId,
data,
requestMetadata: ctx.metadata,
});
const mappedDocument = {
...envelope,
id: mapSecondaryIdToDocumentId(envelope.secondaryId),
};
return mappedDocument;
});

View File

@ -45,6 +45,7 @@ export const ZUpdateDocumentRequestSchema = z.object({
globalAccessAuth: z.array(ZDocumentAccessAuthTypesSchema).optional(),
globalActionAuth: z.array(ZDocumentActionAuthTypesSchema).optional(),
useLegacyFieldInsertion: z.boolean().optional(),
folderId: z.string().nullish(),
})
.optional(),
meta: z

View File

@ -68,7 +68,7 @@ export const applyMultiSignSignatureRoute = procedure
const signatureFields = await prisma.field.findMany({
where: {
documentId: envelope.document.id,
envelopeId: envelope.document.id,
recipientId: envelope.recipient.id,
type: FieldType.SIGNATURE,
inserted: false,

View File

@ -1,6 +1,9 @@
import { EnvelopeType } from '@prisma/client';
import { AppError, AppErrorCode } from '@documenso/lib/errors/app-error';
import { createDocumentV2 } from '@documenso/lib/server-only/document/create-document-v2';
import { verifyEmbeddingPresignToken } from '@documenso/lib/server-only/embedding-presign/verify-embedding-presign-token';
import { createEnvelope } from '@documenso/lib/server-only/envelope/create-envelope';
import { mapSecondaryIdToDocumentId } from '@documenso/lib/utils/envelope';
import { procedure } from '../trpc';
import {
@ -29,27 +32,34 @@ export const createEmbeddingDocumentRoute = procedure
const { title, documentDataId, externalId, recipients, meta } = input;
const document = await createDocumentV2({
const envelope = await createEnvelope({
data: {
type: EnvelopeType.DOCUMENT,
title,
externalId,
recipients,
envelopeItems: [
{
documentDataId,
},
],
},
meta,
documentDataId,
userId: apiToken.userId,
teamId: apiToken.teamId ?? undefined,
requestMetadata: metadata,
});
if (!document.id) {
if (!envelope.id) {
throw new AppError(AppErrorCode.UNKNOWN_ERROR, {
message: 'Failed to create document: missing document ID',
});
}
const legacyDocumentId = mapSecondaryIdToDocumentId(envelope.secondaryId);
return {
documentId: document.id,
documentId: legacyDocumentId,
};
} catch (error) {
if (error instanceof AppError) {

View File

@ -1,6 +1,9 @@
import { EnvelopeType } from '@prisma/client';
import { AppError, AppErrorCode } from '@documenso/lib/errors/app-error';
import { verifyEmbeddingPresignToken } from '@documenso/lib/server-only/embedding-presign/verify-embedding-presign-token';
import { createTemplate } from '@documenso/lib/server-only/template/create-template';
import { createEnvelope } from '@documenso/lib/server-only/envelope/create-envelope';
import { mapSecondaryIdToTemplateId } from '@documenso/lib/utils/envelope';
import { prisma } from '@documenso/prisma';
import { procedure } from '../trpc';
@ -12,7 +15,7 @@ import {
export const createEmbeddingTemplateRoute = procedure
.input(ZCreateEmbeddingTemplateRequestSchema)
.output(ZCreateEmbeddingTemplateResponseSchema)
.mutation(async ({ input, ctx: { req } }) => {
.mutation(async ({ input, ctx: { req, metadata } }) => {
try {
const authorizationHeader = req.headers.get('authorization');
@ -31,20 +34,30 @@ export const createEmbeddingTemplateRoute = procedure
const { title, documentDataId, recipients, meta } = input;
// First create the template
const template = await createTemplate({
const template = await createEnvelope({
userId: apiToken.userId,
data: {
title,
},
templateDocumentDataId: documentDataId,
teamId: apiToken.teamId ?? undefined,
data: {
type: EnvelopeType.TEMPLATE,
title,
envelopeItems: [
{
documentDataId,
},
],
},
meta, // Todo: Migration - Test this.
requestMetadata: metadata,
});
// Todo: Envelopes - Support multiple items.
const firstEnvelopeItem = template.envelopeItems[0];
await Promise.all(
recipients.map(async (recipient) => {
const createdRecipient = await prisma.recipient.create({
data: {
templateId: template.id,
envelopeId: template.id,
email: recipient.email,
name: recipient.name || '',
role: recipient.role || 'SIGNER',
@ -57,6 +70,8 @@ export const createEmbeddingTemplateRoute = procedure
const createdFields = await prisma.field.createMany({
data: fields.map((field) => ({
envelopeId: template.id,
envelopeItemId: firstEnvelopeItem.id,
recipientId: createdRecipient.id,
type: field.type,
page: field.pageNumber,
@ -66,7 +81,6 @@ export const createEmbeddingTemplateRoute = procedure
height: field.height,
customText: '',
inserted: false,
templateId: template.id,
})),
});
@ -77,37 +91,6 @@ export const createEmbeddingTemplateRoute = procedure
}),
);
// Update the template meta if needed
if (meta) {
const upsertMetaData = {
subject: meta.subject,
message: meta.message,
timezone: meta.timezone,
dateFormat: meta.dateFormat,
distributionMethod: meta.distributionMethod,
signingOrder: meta.signingOrder,
redirectUrl: meta.redirectUrl,
language: meta.language,
typedSignatureEnabled: meta.typedSignatureEnabled,
drawSignatureEnabled: meta.drawSignatureEnabled,
uploadSignatureEnabled: meta.uploadSignatureEnabled,
emailSettings: meta.emailSettings,
};
await prisma.documentMeta.upsert({
where: {
templateId: template.id,
},
create: {
templateId: template.id,
...upsertMetaData,
},
update: {
...upsertMetaData,
},
});
}
if (!template.id) {
throw new AppError(AppErrorCode.UNKNOWN_ERROR, {
message: 'Failed to create template: missing template ID',
@ -115,7 +98,7 @@ export const createEmbeddingTemplateRoute = procedure
}
return {
templateId: template.id,
templateId: mapSecondaryIdToTemplateId(template.secondaryId),
};
} catch (error) {
if (error instanceof AppError) {

View File

@ -25,9 +25,7 @@ export const ZGetMultiSignDocumentResponseSchema = ZDocumentLiteSchema.extend({
subject: true,
message: true,
timezone: true,
password: true,
dateFormat: true,
documentId: true,
redirectUrl: true,
typedSignatureEnabled: true,
uploadSignatureEnabled: true,

View File

@ -1,5 +1,5 @@
import { AppError, AppErrorCode } from '@documenso/lib/errors/app-error';
import { upsertDocumentMeta } from '@documenso/lib/server-only/document-meta/upsert-document-meta';
import { updateDocumentMeta } from '@documenso/lib/server-only/document-meta/upsert-document-meta';
import { updateDocument } from '@documenso/lib/server-only/document/update-document';
import { verifyEmbeddingPresignToken } from '@documenso/lib/server-only/embedding-presign/verify-embedding-presign-token';
import { setFieldsForDocument } from '@documenso/lib/server-only/field/set-fields-for-document';
@ -40,8 +40,11 @@ export const updateEmbeddingDocumentRoute = procedure
const { documentId, title, externalId, recipients, meta } = input;
if (meta && Object.values(meta).length > 0) {
await upsertDocumentMeta({
documentId: documentId,
await updateDocumentMeta({
id: {
type: 'documentId',
id: documentId,
},
userId: apiToken.userId,
teamId: apiToken.teamId ?? undefined,
...meta,

View File

@ -57,7 +57,10 @@ export const updateEmbeddingTemplateRoute = procedure
const { recipients: updatedRecipients } = await setTemplateRecipients({
userId: apiToken.userId,
teamId: apiToken.teamId ?? undefined,
templateId,
id: {
type: 'templateId',
id: templateId,
},
recipients: recipientsWithClientId.map((recipient) => ({
id: recipient.id,
email: recipient.email,

View File

@ -1,5 +1,6 @@
import { createDocumentFields } from '@documenso/lib/server-only/field/create-document-fields';
import { createTemplateFields } from '@documenso/lib/server-only/field/create-template-fields';
import { EnvelopeType } from '@prisma/client';
import { createEnvelopeFields } from '@documenso/lib/server-only/field/create-envelope-fields';
import { deleteDocumentField } from '@documenso/lib/server-only/field/delete-document-field';
import { deleteTemplateField } from '@documenso/lib/server-only/field/delete-template-field';
import { getFieldById } from '@documenso/lib/server-only/field/get-field-by-id';
@ -72,6 +73,7 @@ export const fieldRouter = router({
userId: ctx.user.id,
teamId,
fieldId,
envelopeType: EnvelopeType.DOCUMENT,
});
}),
@ -100,10 +102,13 @@ export const fieldRouter = router({
},
});
const createdFields = await createDocumentFields({
const createdFields = await createEnvelopeFields({
userId: ctx.user.id,
teamId,
documentId,
id: {
type: 'documentId',
id: documentId,
},
fields: [field],
requestMetadata: ctx.metadata,
});
@ -136,10 +141,13 @@ export const fieldRouter = router({
},
});
return await createDocumentFields({
return await createEnvelopeFields({
userId: ctx.user.id,
teamId,
documentId,
id: {
type: 'documentId',
id: documentId,
},
fields,
requestMetadata: ctx.metadata,
});
@ -312,11 +320,15 @@ export const fieldRouter = router({
},
});
const createdFields = await createTemplateFields({
const createdFields = await createEnvelopeFields({
userId: ctx.user.id,
teamId,
templateId,
id: {
type: 'templateId',
id: templateId,
},
fields: [field],
requestMetadata: ctx.metadata,
});
return createdFields.fields[0];
@ -352,6 +364,7 @@ export const fieldRouter = router({
userId: ctx.user.id,
teamId,
fieldId,
envelopeType: EnvelopeType.TEMPLATE,
});
}),
@ -380,11 +393,15 @@ export const fieldRouter = router({
},
});
return await createTemplateFields({
return await createEnvelopeFields({
userId: ctx.user.id,
teamId,
templateId,
id: {
type: 'templateId',
id: templateId,
},
fields,
requestMetadata: ctx.metadata,
});
}),

View File

@ -4,13 +4,10 @@ import { deleteFolder } from '@documenso/lib/server-only/folder/delete-folder';
import { findFolders } from '@documenso/lib/server-only/folder/find-folders';
import { getFolderBreadcrumbs } from '@documenso/lib/server-only/folder/get-folder-breadcrumbs';
import { getFolderById } from '@documenso/lib/server-only/folder/get-folder-by-id';
import { moveDocumentToFolder } from '@documenso/lib/server-only/folder/move-document-to-folder';
import { moveFolder } from '@documenso/lib/server-only/folder/move-folder';
import { moveTemplateToFolder } from '@documenso/lib/server-only/folder/move-template-to-folder';
import { pinFolder } from '@documenso/lib/server-only/folder/pin-folder';
import { unpinFolder } from '@documenso/lib/server-only/folder/unpin-folder';
import { updateFolder } from '@documenso/lib/server-only/folder/update-folder';
import { FolderType } from '@documenso/lib/types/folder-type';
import { authenticatedProcedure, router } from '../trpc';
import {
@ -21,9 +18,7 @@ import {
ZGenericSuccessResponse,
ZGetFoldersResponseSchema,
ZGetFoldersSchema,
ZMoveDocumentToFolderSchema,
ZMoveFolderSchema,
ZMoveTemplateToFolderSchema,
ZPinFolderSchema,
ZSuccessResponseSchema,
ZUnpinFolderSchema,
@ -266,95 +261,6 @@ export const folderRouter = router({
};
}),
/**
* @private
*/
moveDocumentToFolder: authenticatedProcedure
.input(ZMoveDocumentToFolderSchema)
.mutation(async ({ input, ctx }) => {
const { teamId, user } = ctx;
const { documentId, folderId } = input;
ctx.logger.info({
input: {
documentId,
folderId,
},
});
if (folderId !== null) {
try {
await getFolderById({
userId: user.id,
teamId,
folderId,
type: FolderType.DOCUMENT,
});
} catch (error) {
throw new AppError(AppErrorCode.NOT_FOUND, {
message: 'Folder not found',
});
}
}
const result = await moveDocumentToFolder({
userId: user.id,
teamId,
documentId,
folderId,
requestMetadata: ctx.metadata,
});
return {
...result,
type: FolderType.DOCUMENT,
};
}),
/**
* @private
*/
moveTemplateToFolder: authenticatedProcedure
.input(ZMoveTemplateToFolderSchema)
.mutation(async ({ input, ctx }) => {
const { teamId, user } = ctx;
const { templateId, folderId } = input;
ctx.logger.info({
input: {
templateId,
folderId,
},
});
if (folderId !== null) {
try {
await getFolderById({
userId: user.id,
teamId,
folderId,
type: FolderType.TEMPLATE,
});
} catch (error) {
throw new AppError(AppErrorCode.NOT_FOUND, {
message: 'Folder not found',
});
}
}
const result = await moveTemplateToFolder({
userId: user.id,
teamId,
templateId,
folderId,
});
return {
...result,
type: FolderType.TEMPLATE,
};
}),
/**
* @private
*/

View File

@ -77,18 +77,6 @@ export const ZMoveFolderSchema = z.object({
type: ZFolderTypeSchema.optional(),
});
export const ZMoveDocumentToFolderSchema = z.object({
documentId: z.number(),
folderId: z.string().nullable().optional(),
type: z.enum(['DOCUMENT']).optional(),
});
export const ZMoveTemplateToFolderSchema = z.object({
templateId: z.number(),
folderId: z.string().nullable().optional(),
type: z.enum(['TEMPLATE']).optional(),
});
export const ZPinFolderSchema = z.object({
folderId: z.string(),
type: ZFolderTypeSchema.optional(),

View File

@ -1,3 +1,5 @@
import { EnvelopeType } from '@prisma/client';
import { completeDocumentWithToken } from '@documenso/lib/server-only/document/complete-document-with-token';
import { rejectDocumentWithToken } from '@documenso/lib/server-only/document/reject-document-with-token';
import { createDocumentRecipients } from '@documenso/lib/server-only/recipient/create-document-recipients';
@ -77,6 +79,7 @@ export const recipientRouter = router({
userId: ctx.user.id,
teamId,
recipientId,
type: EnvelopeType.DOCUMENT,
});
}),
@ -108,7 +111,10 @@ export const recipientRouter = router({
const createdRecipients = await createDocumentRecipients({
userId: ctx.user.id,
teamId,
documentId,
id: {
type: 'documentId',
id: documentId,
},
recipients: [recipient],
requestMetadata: ctx.metadata,
});
@ -144,7 +150,10 @@ export const recipientRouter = router({
return await createDocumentRecipients({
userId: ctx.user.id,
teamId,
documentId,
id: {
type: 'documentId',
id: documentId,
},
recipients,
requestMetadata: ctx.metadata,
});
@ -178,7 +187,10 @@ export const recipientRouter = router({
const updatedRecipients = await updateDocumentRecipients({
userId: ctx.user.id,
teamId,
documentId,
id: {
type: 'documentId',
id: documentId,
},
recipients: [recipient],
requestMetadata: ctx.metadata,
});
@ -214,7 +226,10 @@ export const recipientRouter = router({
return await updateDocumentRecipients({
userId: ctx.user.id,
teamId,
documentId,
id: {
type: 'documentId',
id: documentId,
},
recipients,
requestMetadata: ctx.metadata,
});
@ -316,6 +331,7 @@ export const recipientRouter = router({
userId: ctx.user.id,
teamId,
recipientId,
type: EnvelopeType.TEMPLATE,
});
}),
@ -507,7 +523,10 @@ export const recipientRouter = router({
return await setTemplateRecipients({
userId: ctx.user.id,
teamId,
templateId,
id: {
type: 'templateId',
id: templateId,
},
recipients: recipients.map((recipient) => ({
id: recipient.nativeId,
email: recipient.email,
@ -533,9 +552,12 @@ export const recipientRouter = router({
},
});
return await completeDocumentWithToken({
await completeDocumentWithToken({
token,
documentId,
id: {
type: 'documentId',
id: documentId,
},
authOptions,
accessAuthOptions,
nextSigner,
@ -560,7 +582,10 @@ export const recipientRouter = router({
return await rejectDocumentWithToken({
token,
documentId,
id: {
type: 'documentId',
id: documentId,
},
reason,
requestMetadata: ctx.metadata.requestMetadata,
});

View File

@ -9,7 +9,6 @@ import { folderRouter } from './folder-router/router';
import { organisationRouter } from './organisation-router/router';
import { profileRouter } from './profile-router/router';
import { recipientRouter } from './recipient-router/router';
import { shareLinkRouter } from './share-link-router/router';
import { teamRouter } from './team-router/router';
import { templateRouter } from './template-router/router';
import { router } from './trpc';
@ -25,7 +24,6 @@ export const appRouter = router({
recipient: recipientRouter,
admin: adminRouter,
organisation: organisationRouter,
shareLink: shareLinkRouter,
apiToken: apiTokenRouter,
team: teamRouter,
template: templateRouter,

View File

@ -1,50 +0,0 @@
import { NEXT_PUBLIC_WEBAPP_URL } from '@documenso/lib/constants/app';
import { buildTeamWhereQuery } from '@documenso/lib/utils/teams';
import { prisma } from '@documenso/prisma';
import { procedure } from '../trpc';
import {
ZGetDocumentInternalUrlForQRCodeInput,
ZGetDocumentInternalUrlForQRCodeOutput,
} from './get-document-internal-url-for-qr-code.types';
export const getDocumentInternalUrlForQRCodeRoute = procedure
.input(ZGetDocumentInternalUrlForQRCodeInput)
.output(ZGetDocumentInternalUrlForQRCodeOutput)
.query(async ({ input, ctx }) => {
const { documentId } = input;
ctx.logger.info({
input: {
documentId,
},
});
if (!ctx.user) {
return null;
}
const document = await prisma.document.findFirst({
where: {
OR: [
{
id: documentId,
userId: ctx.user.id,
},
{
id: documentId,
team: buildTeamWhereQuery({ teamId: undefined, userId: ctx.user.id }),
},
],
},
include: {
team: true,
},
});
if (!document) {
return null;
}
return `${NEXT_PUBLIC_WEBAPP_URL()}/t/${document.team.url}/documents/${document.id}`;
});

View File

@ -1,15 +0,0 @@
import { z } from 'zod';
export const ZGetDocumentInternalUrlForQRCodeInput = z.object({
documentId: z.number(),
});
export type TGetDocumentInternalUrlForQRCodeInput = z.infer<
typeof ZGetDocumentInternalUrlForQRCodeInput
>;
export const ZGetDocumentInternalUrlForQRCodeOutput = z.string().nullable();
export type TGetDocumentInternalUrlForQRCodeOutput = z.infer<
typeof ZGetDocumentInternalUrlForQRCodeOutput
>;

View File

@ -1,33 +0,0 @@
import { createOrGetShareLink } from '@documenso/lib/server-only/share/create-or-get-share-link';
import { procedure, router } from '../trpc';
import { getDocumentInternalUrlForQRCodeRoute } from './get-document-internal-url-for-qr-code';
import { ZCreateOrGetShareLinkMutationSchema } from './schema';
export const shareLinkRouter = router({
createOrGetShareLink: procedure
.input(ZCreateOrGetShareLinkMutationSchema)
.mutation(async ({ ctx, input }) => {
const { documentId, token } = input;
ctx.logger.info({
input: {
documentId,
},
});
if (token) {
return await createOrGetShareLink({ documentId, token });
}
if (!ctx.user?.id) {
throw new Error(
'You must either provide a token or be logged in to create a sharing link.',
);
}
return await createOrGetShareLink({ documentId, userId: ctx.user.id });
}),
getDocumentInternalUrlForQRCode: getDocumentInternalUrlForQRCodeRoute,
});

View File

@ -1,10 +0,0 @@
import { z } from 'zod';
export const ZCreateOrGetShareLinkMutationSchema = z.object({
documentId: z.number(),
token: z.string().optional(),
});
export type TCreateOrGetShareLinkMutationSchema = z.infer<
typeof ZCreateOrGetShareLinkMutationSchema
>;

View File

@ -1,5 +1,5 @@
import type { Document } from '@prisma/client';
import { DocumentDataType } from '@prisma/client';
import type { Envelope } from '@prisma/client';
import { DocumentDataType, EnvelopeType } from '@prisma/client';
import { getServerLimits } from '@documenso/ee/server-only/limits/server';
import { AppError, AppErrorCode } from '@documenso/lib/errors/app-error';
@ -7,15 +7,12 @@ import { jobs } from '@documenso/lib/jobs/client';
import { createDocumentData } from '@documenso/lib/server-only/document-data/create-document-data';
import { getDocumentWithDetailsById } from '@documenso/lib/server-only/document/get-document-with-details-by-id';
import { sendDocument } from '@documenso/lib/server-only/document/send-document';
import { createEnvelope } from '@documenso/lib/server-only/envelope/create-envelope';
import {
ZCreateDocumentFromDirectTemplateResponseSchema,
createDocumentFromDirectTemplate,
} from '@documenso/lib/server-only/template/create-document-from-direct-template';
import { createDocumentFromTemplate } from '@documenso/lib/server-only/template/create-document-from-template';
import {
ZCreateTemplateResponseSchema,
createTemplate,
} from '@documenso/lib/server-only/template/create-template';
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';
@ -25,6 +22,8 @@ import { getTemplateById } from '@documenso/lib/server-only/template/get-templat
import { toggleTemplateDirectLink } from '@documenso/lib/server-only/template/toggle-template-direct-link';
import { updateTemplate } from '@documenso/lib/server-only/template/update-template';
import { getPresignPostUrl } from '@documenso/lib/universal/upload/server-actions';
import { mapSecondaryIdToTemplateId } from '@documenso/lib/utils/envelope';
import { mapEnvelopeToTemplateLite } from '@documenso/lib/utils/templates';
import { ZGenericSuccessResponse, ZSuccessResponseSchema } from '../document-router/schema';
import { authenticatedProcedure, maybeAuthenticatedProcedure, router } from '../trpc';
@ -36,6 +35,7 @@ import {
ZCreateTemplateDirectLinkRequestSchema,
ZCreateTemplateDirectLinkResponseSchema,
ZCreateTemplateMutationSchema,
ZCreateTemplateResponseSchema,
ZCreateTemplateV2RequestSchema,
ZCreateTemplateV2ResponseSchema,
ZDeleteTemplateDirectLinkRequestSchema,
@ -77,11 +77,41 @@ export const templateRouter = router({
},
});
return await findTemplates({
const result = await findTemplates({
userId: ctx.user.id,
teamId,
...input,
});
// Remapping for backwards compatibility.
return {
...result,
data: result.data.map((envelope) => {
const legacyTemplateId = mapSecondaryIdToTemplateId(envelope.secondaryId);
return {
id: legacyTemplateId,
type: envelope.templateType,
visibility: envelope.visibility,
externalId: envelope.externalId,
title: envelope.title,
userId: envelope.userId,
teamId: envelope.teamId,
authOptions: envelope.authOptions,
createdAt: envelope.createdAt,
updatedAt: envelope.updatedAt,
publicTitle: envelope.publicTitle,
publicDescription: envelope.publicDescription,
folderId: envelope.folderId,
useLegacyFieldInsertion: envelope.useLegacyFieldInsertion,
team: envelope.team,
fields: envelope.fields,
recipients: envelope.recipients,
templateMeta: envelope.documentMeta,
directLink: envelope.directLink,
};
}),
};
}),
/**
@ -121,7 +151,7 @@ export const templateRouter = router({
* @private
*/
createTemplate: authenticatedProcedure
// .meta({
// .meta({ // Note before releasing this to public, update the response schema to be correct.
// openapi: {
// method: 'POST',
// path: '/template/create',
@ -142,15 +172,25 @@ export const templateRouter = router({
},
});
return await createTemplate({
const envelope = await createEnvelope({
userId: ctx.user.id,
teamId,
templateDocumentDataId,
data: {
type: EnvelopeType.TEMPLATE,
title,
folderId,
envelopeItems: [
{
documentDataId: templateDocumentDataId,
},
],
},
requestMetadata: ctx.metadata,
});
return {
legacyTemplateId: mapSecondaryIdToTemplateId(envelope.secondaryId),
};
}),
/**
@ -197,26 +237,34 @@ export const templateRouter = router({
type: DocumentDataType.S3_PATH,
});
const createdTemplate = await createTemplate({
const createdTemplate = await createEnvelope({
userId: user.id,
teamId,
templateDocumentDataId: templateDocumentData.id,
data: {
type: EnvelopeType.TEMPLATE,
title,
envelopeItems: [
{
documentDataId: templateDocumentData.id,
},
],
folderId,
externalId,
externalId: externalId ?? undefined,
visibility,
globalAccessAuth,
globalActionAuth,
templateType: type,
publicTitle,
publicDescription,
type,
},
meta,
requestMetadata: ctx.metadata,
});
const legacyTemplateId = mapSecondaryIdToTemplateId(createdTemplate.secondaryId);
const fullTemplate = await getTemplateById({
id: createdTemplate.id,
id: legacyTemplateId,
userId: user.id,
teamId,
});
@ -252,13 +300,15 @@ export const templateRouter = router({
},
});
return await updateTemplate({
const envelope = await updateTemplate({
userId,
teamId,
templateId,
data,
meta,
});
return mapEnvelopeToTemplateLite(envelope);
}),
/**
@ -285,11 +335,16 @@ export const templateRouter = router({
},
});
return await duplicateTemplate({
const envelope = await duplicateTemplate({
userId: ctx.user.id,
teamId,
templateId,
id: {
type: 'templateId',
id: templateId,
},
});
return mapEnvelopeToTemplateLite(envelope);
}),
/**
@ -360,8 +415,11 @@ export const templateRouter = router({
throw new Error('You have reached your document limit.');
}
const document: Document = await createDocumentFromTemplate({
templateId,
const envelope: Envelope = await createDocumentFromTemplate({
id: {
type: 'templateId',
id: templateId,
},
teamId,
userId: ctx.user.id,
recipients,
@ -373,7 +431,10 @@ export const templateRouter = router({
if (distributeDocument) {
await sendDocument({
documentId: document.id,
id: {
type: 'envelopeId',
id: envelope.id,
},
userId: ctx.user.id,
teamId,
requestMetadata: ctx.metadata,
@ -385,7 +446,10 @@ export const templateRouter = router({
}
return getDocumentWithDetailsById({
documentId: document.id,
id: {
type: 'envelopeId',
id: envelope.id,
},
userId: ctx.user.id,
teamId,
});
@ -480,7 +544,15 @@ export const templateRouter = router({
});
}
return await createTemplateDirectLink({ userId, teamId, templateId, directRecipientId });
return await createTemplateDirectLink({
userId,
teamId,
id: {
type: 'templateId',
id: templateId,
},
directRecipientId,
});
}),
/**

View File

@ -146,11 +146,13 @@ export const ZCreateTemplateDirectLinkRequestSchema = z.object({
const GenericDirectLinkResponseSchema = TemplateDirectLinkSchema.pick({
id: true,
templateId: true,
token: true,
createdAt: true,
enabled: true,
directTemplateRecipientId: true,
// envelopeId: true, // Todo: Envelopes
}).extend({
templateId: z.number(),
});
export const ZCreateTemplateDirectLinkResponseSchema = GenericDirectLinkResponseSchema;
@ -194,6 +196,10 @@ export const ZCreateTemplateV2ResponseSchema = z.object({
uploadUrl: z.string().min(1),
});
export const ZCreateTemplateResponseSchema = z.object({
legacyTemplateId: z.number(),
});
export const ZUpdateTemplateRequestSchema = z.object({
templateId: z.number(),
data: z
@ -207,6 +213,7 @@ export const ZUpdateTemplateRequestSchema = z.object({
publicDescription: ZTemplatePublicDescriptionSchema.optional(),
type: z.nativeEnum(TemplateType).optional(),
useLegacyFieldInsertion: z.boolean().optional(),
folderId: z.string().nullish(),
})
.optional(),
meta: ZTemplateMetaUpsertSchema.optional(),