diff --git a/apps/remix/app/components/tables/admin-dashboard-users-table.tsx b/apps/remix/app/components/tables/admin-dashboard-users-table.tsx index 6c3919b4c..c409f7d2c 100644 --- a/apps/remix/app/components/tables/admin-dashboard-users-table.tsx +++ b/apps/remix/app/components/tables/admin-dashboard-users-table.tsx @@ -2,7 +2,7 @@ import { useEffect, useMemo, useState, useTransition } from 'react'; import { msg } from '@lingui/core/macro'; import { useLingui } from '@lingui/react'; -import type { Document, Role, Subscription } from '@prisma/client'; +import type { Role, Subscription } from '@prisma/client'; import { Edit, Loader } from 'lucide-react'; import { Link } from 'react-router'; @@ -20,7 +20,7 @@ type UserData = { email: string; roles: Role[]; subscriptions?: SubscriptionLite[] | null; - documents: DocumentLite[]; + documentCount: number; }; type SubscriptionLite = Pick< @@ -28,8 +28,6 @@ type SubscriptionLite = Pick< 'id' | 'status' | 'planId' | 'priceId' | 'createdAt' | 'periodEnd' >; -type DocumentLite = Pick; - type AdminDashboardUsersTableProps = { users: UserData[]; totalPages: number; @@ -74,10 +72,7 @@ export const AdminDashboardUsersTable = ({ }, { header: _(msg`Documents`), - accessorKey: 'documents', - cell: ({ row }) => { - return
{row.original.documents?.length}
; - }, + accessorKey: 'documentCount', }, { header: '', diff --git a/packages/lib/server-only/document/get-document-by-id.ts b/packages/lib/server-only/document/get-document-by-id.ts index 542e6e3cb..383dc201d 100644 --- a/packages/lib/server-only/document/get-document-by-id.ts +++ b/packages/lib/server-only/document/get-document-by-id.ts @@ -1,39 +1,32 @@ -import type { Prisma } from '@prisma/client'; -import { DocumentStatus, TeamMemberRole } from '@prisma/client'; -import { match } from 'ts-pattern'; - import { prisma } from '@documenso/prisma'; import { AppError, AppErrorCode } from '../../errors/app-error'; -import { DocumentVisibility } from '../../types/document-visibility'; -import { getTeamById } from '../team/get-team'; +import { getEnvelopeWhereInput } from '../envelope/get-envelope-by-id'; export type GetDocumentByIdOptions = { documentId: number; userId: number; teamId: number; - folderId?: string; }; -export const getDocumentById = async ({ - documentId, - userId, - teamId, - folderId, -}: GetDocumentByIdOptions) => { - const { documentWhereInput } = await getDocumentWhereInput({ - documentId, - userId, - teamId, +export const getDocumentById = async ({ documentId, userId, teamId }: GetDocumentByIdOptions) => { + const { documentWhereInput } = await getEnvelopeWhereInput({ + id: { + type: 'documentId', + id: documentId, + }, + validatedUserId: userId, + unvalidatedTeamId: teamId, }); - const document = await prisma.document.findFirst({ - where: { - ...documentWhereInput, - folderId, - }, + const envelope = await prisma.envelope.findFirst({ + where: documentWhereInput, include: { - documentData: true, + documents: { + include: { + documentData: true, + }, + }, documentMeta: true, user: { select: { @@ -56,7 +49,7 @@ export const getDocumentById = async ({ }, }); - if (!document) { + if (!envelope) { throw new AppError(AppErrorCode.NOT_FOUND, { message: 'Document could not be found', }); @@ -64,93 +57,3 @@ export const getDocumentById = async ({ return document; }; - -export type GetDocumentWhereInputOptions = { - documentId: number; - userId: number; - teamId: number; -}; - -/** - * Generate the where input for a given Prisma document query. - * - * This will return a query that allows a user to get a document if they have valid access to it. - */ -export const getDocumentWhereInput = async ({ - documentId, - userId, - teamId, -}: GetDocumentWhereInputOptions) => { - const team = await getTeamById({ teamId, userId }); - - const user = await prisma.user.findFirstOrThrow({ - where: { - id: userId, - }, - }); - - const teamVisibilityFilters = match(team.currentTeamRole) - .with(TeamMemberRole.ADMIN, () => [ - DocumentVisibility.EVERYONE, - DocumentVisibility.MANAGER_AND_ABOVE, - DocumentVisibility.ADMIN, - ]) - .with(TeamMemberRole.MANAGER, () => [ - DocumentVisibility.EVERYONE, - DocumentVisibility.MANAGER_AND_ABOVE, - ]) - .otherwise(() => [DocumentVisibility.EVERYONE]); - - const documentOrInput: Prisma.DocumentWhereInput[] = [ - // Allow access if they own the document. - { - userId, - }, - // Or, if they belong to the team that the document is associated with. - { - visibility: { - in: teamVisibilityFilters, - }, - teamId: team.id, - }, - // Or, if they are a recipient of the document. - { - status: { - not: DocumentStatus.DRAFT, - }, - recipients: { - some: { - email: user.email, - }, - }, - }, - ]; - - // Allow access to documents sent to or from the team email. - if (team.teamEmail) { - documentOrInput.push( - { - recipients: { - some: { - email: team.teamEmail.email, - }, - }, - }, - { - user: { - email: team.teamEmail.email, - }, - }, - ); - } - - const documentWhereInput: Prisma.DocumentWhereUniqueInput = { - id: documentId, - OR: documentOrInput, - }; - - return { - documentWhereInput, - team, - }; -}; diff --git a/packages/lib/server-only/envelope/get-envelope-by-id.ts b/packages/lib/server-only/envelope/get-envelope-by-id.ts new file mode 100644 index 000000000..06c575731 --- /dev/null +++ b/packages/lib/server-only/envelope/get-envelope-by-id.ts @@ -0,0 +1,157 @@ +import type { Prisma } from '@prisma/client'; +import { TeamMemberRole } from '@prisma/client'; +import { match } from 'ts-pattern'; + +import { prisma } from '@documenso/prisma'; + +import { AppError, AppErrorCode } from '../../errors/app-error'; +import { DocumentVisibility } from '../../types/document-visibility'; +import type { EnvelopeIdOptions } from '../../utils/envelope'; +import { buildEnvelopeIdQuery } from '../../utils/envelope'; +import { getTeamById } from '../team/get-team'; + +export type GetEnvelopeByIdOptions = { + id: EnvelopeIdOptions; + + userId: number; + teamId: number; +}; + +export const getEnvelopeById = async ({ id, userId, teamId }: GetEnvelopeByIdOptions) => { + const { documentWhereInput } = await getEnvelopeWhereInput({ + id, + validatedUserId: userId, + unvalidatedTeamId: teamId, + }); + + const document = await prisma.envelope.findFirst({ + where: documentWhereInput, + include: { + documents: { + include: { + documentData: true, + }, + }, + documentMeta: true, + user: { + select: { + id: true, + name: true, + email: true, + }, + }, + recipients: { + select: { + email: true, + }, + }, + team: { + select: { + id: true, + url: true, + }, + }, + }, + }); + + if (!document) { + throw new AppError(AppErrorCode.NOT_FOUND, { + message: 'Document could not be found', + }); + } + + return document; +}; + +export type GetEnvelopeWhereInputOptions = { + id: EnvelopeIdOptions; + + /** + * The user ID who has been authenticated. + */ + validatedUserId: number; + + /** + * The unknown teamId from the request. + */ + unvalidatedTeamId: number; +}; + +/** + * Generate the where input for a given Prisma envelope query. + * + * This will return a query that allows a user to get a document if they have valid access to it. + */ +export const getEnvelopeWhereInput = async ({ + id, + validatedUserId, + unvalidatedTeamId, +}: GetEnvelopeWhereInputOptions) => { + const team = await getTeamById({ teamId: unvalidatedTeamId, userId: validatedUserId }); + + const teamVisibilityFilters = match(team.currentTeamRole) + .with(TeamMemberRole.ADMIN, () => [ + DocumentVisibility.EVERYONE, + DocumentVisibility.MANAGER_AND_ABOVE, + DocumentVisibility.ADMIN, + ]) + .with(TeamMemberRole.MANAGER, () => [ + DocumentVisibility.EVERYONE, + DocumentVisibility.MANAGER_AND_ABOVE, + ]) + .otherwise(() => [DocumentVisibility.EVERYONE]); + + const documentOrInput: Prisma.EnvelopeWhereInput[] = [ + // Allow access if they own the document. + { + userId: validatedUserId, + }, + // Or, if they belong to the team that the document is associated with. + { + visibility: { + in: teamVisibilityFilters, + }, + teamId: team.id, + }, + // Or, if they are a recipient of the document. + // ????????????? should recipients be able to do X? + // { + // status: { + // not: DocumentStatus.DRAFT, + // }, + // recipients: { + // some: { + // email: user.email, + // }, + // }, + // }, + ]; + + // Allow access to documents sent to or from the team email. + if (team.teamEmail) { + documentOrInput.push( + { + recipients: { + some: { + email: team.teamEmail.email, + }, + }, + }, + { + user: { + email: team.teamEmail.email, + }, + }, + ); + } + + const documentWhereInput: Prisma.EnvelopeWhereUniqueInput = { + ...buildEnvelopeIdQuery(id), + OR: documentOrInput, + }; + + return { + documentWhereInput, + team, + }; +}; diff --git a/packages/lib/server-only/user/delete-user.ts b/packages/lib/server-only/user/delete-user.ts index 414e13a46..ccc7f7476 100644 --- a/packages/lib/server-only/user/delete-user.ts +++ b/packages/lib/server-only/user/delete-user.ts @@ -1,4 +1,4 @@ -import { DocumentStatus } from '@prisma/client'; +import { DocumentStatus, EnvelopeType } from '@prisma/client'; import { prisma } from '@documenso/prisma'; @@ -25,9 +25,10 @@ export const deleteUser = async ({ id }: DeleteUserOptions) => { const serviceAccount = await deletedAccountServiceAccount(); // TODO: Send out cancellations for all pending docs - await prisma.document.updateMany({ + await prisma.envelope.updateMany({ where: { userId: user.id, + type: EnvelopeType.DOCUMENT, status: { in: [DocumentStatus.PENDING, DocumentStatus.REJECTED, DocumentStatus.COMPLETED], }, diff --git a/packages/lib/server-only/user/get-all-users.ts b/packages/lib/server-only/user/get-all-users.ts index 69e07813d..fd3c560db 100644 --- a/packages/lib/server-only/user/get-all-users.ts +++ b/packages/lib/server-only/user/get-all-users.ts @@ -1,4 +1,4 @@ -import { Prisma } from '@prisma/client'; +import { EnvelopeType, Prisma } from '@prisma/client'; import { prisma } from '@documenso/prisma'; @@ -34,12 +34,20 @@ export const findUsers = async ({ const [users, count] = await Promise.all([ prisma.user.findMany({ - include: { - documents: { + select: { + _count: { select: { - id: true, + envelopes: { + where: { + type: EnvelopeType.DOCUMENT, + }, + }, }, }, + id: true, + name: true, + email: true, + roles: true, }, where: whereClause, skip: Math.max(page - 1, 0) * perPage, @@ -51,7 +59,10 @@ export const findUsers = async ({ ]); return { - users, + users: users.map((user) => ({ + ...user, + documentCount: user._count.envelopes, + })), totalPages: Math.ceil(count / perPage), }; }; diff --git a/packages/lib/types/document.ts b/packages/lib/types/document.ts index b42801b72..1fa968eb4 100644 --- a/packages/lib/types/document.ts +++ b/packages/lib/types/document.ts @@ -1,11 +1,11 @@ -import type { z } from 'zod'; +import { z } from 'zod'; import { DocumentDataSchema } from '@documenso/prisma/generated/zod/modelSchema/DocumentDataSchema'; import { DocumentMetaSchema } from '@documenso/prisma/generated/zod/modelSchema/DocumentMetaSchema'; -import { DocumentSchema } from '@documenso/prisma/generated/zod/modelSchema/DocumentSchema'; import { FolderSchema } from '@documenso/prisma/generated/zod/modelSchema/FolderSchema'; import { TeamSchema } from '@documenso/prisma/generated/zod/modelSchema/TeamSchema'; import { UserSchema } from '@documenso/prisma/generated/zod/modelSchema/UserSchema'; +import { LegacyDocumentSchema } from '@documenso/prisma/types/document-legacy-schema'; import { ZFieldSchema } from './field'; import { ZRecipientLiteSchema } from './recipient'; @@ -15,7 +15,7 @@ import { ZRecipientLiteSchema } from './recipient'; * * Mainly used for returning a single document from the API. */ -export const ZDocumentSchema = DocumentSchema.pick({ +export const ZDocumentSchema = LegacyDocumentSchema.pick({ visibility: true, status: true, source: true, @@ -31,9 +31,12 @@ export const ZDocumentSchema = DocumentSchema.pick({ completedAt: true, deletedAt: true, teamId: true, - templateId: true, folderId: true, }).extend({ + // Which "Template" the document was created from. Legacy field for backwards compatibility. + // The actual field is now called `createdFromDocumentId`. + templateId: z.number().optional(), + // Todo: Maybe we want to alter this a bit since this returns a lot of data. documentData: DocumentDataSchema.pick({ type: true, @@ -82,7 +85,7 @@ export type TDocument = z.infer; /** * A lite version of the document response schema without relations. */ -export const ZDocumentLiteSchema = DocumentSchema.pick({ +export const ZDocumentLiteSchema = LegacyDocumentSchema.pick({ visibility: true, status: true, source: true, @@ -98,9 +101,12 @@ export const ZDocumentLiteSchema = DocumentSchema.pick({ completedAt: true, deletedAt: true, teamId: true, - templateId: true, folderId: true, useLegacyFieldInsertion: true, +}).extend({ + // Which "Template" the document was created from. Legacy field for backwards compatibility. + // The actual field is now called `createdFromDocumentId`. + templateId: z.number().optional(), }); export type TDocumentLite = z.infer; @@ -108,7 +114,7 @@ export type TDocumentLite = z.infer; /** * A version of the document response schema when returning multiple documents at once from a single API endpoint. */ -export const ZDocumentManySchema = DocumentSchema.pick({ +export const ZDocumentManySchema = LegacyDocumentSchema.pick({ visibility: true, status: true, source: true, @@ -124,10 +130,13 @@ export const ZDocumentManySchema = DocumentSchema.pick({ completedAt: true, deletedAt: true, teamId: true, - templateId: true, folderId: true, useLegacyFieldInsertion: true, }).extend({ + // Which "Template" the document was created from. Legacy field for backwards compatibility. + // The actual field is now called `createdFromDocumentId`. + templateId: z.number().optional(), + user: UserSchema.pick({ id: true, name: true, diff --git a/packages/lib/types/field.ts b/packages/lib/types/field.ts index 5b3839f0c..7976e6537 100644 --- a/packages/lib/types/field.ts +++ b/packages/lib/types/field.ts @@ -18,8 +18,6 @@ export const ZFieldSchema = FieldSchema.pick({ type: true, id: true, secondaryId: true, - documentId: true, - templateId: true, recipientId: true, page: true, positionX: true, @@ -29,6 +27,10 @@ export const ZFieldSchema = FieldSchema.pick({ customText: true, inserted: true, fieldMeta: true, +}).extend({ + // Todo: Decide whether to make these two IDs backwards compatible. + documentId: z.number().optional(), + templateId: z.number().optional(), }); export const ZFieldPageNumberSchema = z diff --git a/packages/lib/types/recipient.ts b/packages/lib/types/recipient.ts index e46681f44..215a8cb02 100644 --- a/packages/lib/types/recipient.ts +++ b/packages/lib/types/recipient.ts @@ -1,3 +1,5 @@ +import { z } from 'zod'; + import { RecipientSchema } from '@documenso/prisma/generated/zod/modelSchema/RecipientSchema'; import { TeamSchema } from '@documenso/prisma/generated/zod/modelSchema/TeamSchema'; import { UserSchema } from '@documenso/prisma/generated/zod/modelSchema/UserSchema'; @@ -15,8 +17,6 @@ export const ZRecipientSchema = RecipientSchema.pick({ signingStatus: true, sendStatus: true, id: true, - documentId: true, - templateId: true, email: true, name: true, token: true, @@ -28,6 +28,10 @@ export const ZRecipientSchema = RecipientSchema.pick({ rejectionReason: true, }).extend({ fields: ZFieldSchema.array(), + + // Todo: Decide whether to make these two IDs backwards compatible. + documentId: z.number().optional(), + templateId: z.number().optional(), }); /** @@ -39,8 +43,6 @@ export const ZRecipientLiteSchema = RecipientSchema.pick({ signingStatus: true, sendStatus: true, id: true, - documentId: true, - templateId: true, email: true, name: true, token: true, @@ -50,6 +52,10 @@ export const ZRecipientLiteSchema = RecipientSchema.pick({ authOptions: true, signingOrder: true, rejectionReason: true, +}).extend({ + // Todo: Decide whether to make these two IDs backwards compatible. + documentId: z.number().optional(), + templateId: z.number().optional(), }); /** @@ -61,8 +67,6 @@ export const ZRecipientManySchema = RecipientSchema.pick({ signingStatus: true, sendStatus: true, id: true, - documentId: true, - templateId: true, email: true, name: true, token: true, @@ -83,4 +87,8 @@ export const ZRecipientManySchema = RecipientSchema.pick({ id: true, url: true, }).nullable(), + + // Todo: Decide whether to make these two IDs backwards compatible. + documentId: z.number().optional(), + templateId: z.number().optional(), }); diff --git a/packages/lib/types/template.ts b/packages/lib/types/template.ts index de6708c7c..ba4a8f7fd 100644 --- a/packages/lib/types/template.ts +++ b/packages/lib/types/template.ts @@ -1,12 +1,12 @@ -import type { z } from 'zod'; +import { z } from 'zod'; import { DocumentDataSchema } from '@documenso/prisma/generated/zod/modelSchema/DocumentDataSchema'; import { DocumentMetaSchema } from '@documenso/prisma/generated/zod/modelSchema/DocumentMetaSchema'; import { FolderSchema } from '@documenso/prisma/generated/zod/modelSchema/FolderSchema'; import TeamSchema from '@documenso/prisma/generated/zod/modelSchema/TeamSchema'; import { TemplateDirectLinkSchema } from '@documenso/prisma/generated/zod/modelSchema/TemplateDirectLinkSchema'; -import { TemplateSchema } from '@documenso/prisma/generated/zod/modelSchema/TemplateSchema'; import { UserSchema } from '@documenso/prisma/generated/zod/modelSchema/UserSchema'; +import { TemplateSchema } from '@documenso/prisma/types/template-legacy-schema'; import { ZFieldSchema } from './field'; import { ZRecipientLiteSchema } from './recipient'; @@ -51,13 +51,17 @@ export const ZTemplateSchema = TemplateSchema.pick({ drawSignatureEnabled: true, allowDictateNextSigner: true, distributionMethod: true, - templateId: true, redirectUrl: true, language: true, emailSettings: true, emailId: true, emailReplyTo: true, - }).nullable(), + }) + .extend({ + // Legacy field for backwards compatibility. Needs to refer to the Envelope `secondaryTemplateId`. + templateId: z.number(), + }) + .nullable(), directLink: TemplateDirectLinkSchema.nullable(), user: UserSchema.pick({ id: true, diff --git a/packages/lib/utils/document.ts b/packages/lib/utils/document.ts index db2310a8f..b371c283c 100644 --- a/packages/lib/utils/document.ts +++ b/packages/lib/utils/document.ts @@ -1,10 +1,10 @@ -import type { Document, DocumentMeta, OrganisationGlobalSettings } from '@prisma/client'; +import type { DocumentMeta, Envelope, OrganisationGlobalSettings } from '@prisma/client'; import { DocumentDistributionMethod, DocumentSigningOrder, DocumentStatus } from '@prisma/client'; import { DEFAULT_DOCUMENT_TIME_ZONE } from '../constants/time-zones'; import { DEFAULT_DOCUMENT_EMAIL_SETTINGS } from '../types/document-email'; -export const isDocumentCompleted = (document: Pick | DocumentStatus) => { +export const isDocumentCompleted = (document: Pick | DocumentStatus) => { const status = typeof document === 'string' ? document : document.status; return status === DocumentStatus.COMPLETED || status === DocumentStatus.REJECTED; @@ -53,5 +53,5 @@ export const extractDerivedDocumentMeta = ( emailReplyTo: meta.emailReplyTo ?? settings.emailReplyTo, emailSettings: meta.emailSettings || settings.emailDocumentSettings || DEFAULT_DOCUMENT_EMAIL_SETTINGS, - } satisfies Omit; + } satisfies Omit; }; diff --git a/packages/lib/utils/envelope.ts b/packages/lib/utils/envelope.ts new file mode 100644 index 000000000..ad48753da --- /dev/null +++ b/packages/lib/utils/envelope.ts @@ -0,0 +1,92 @@ +import { EnvelopeType } from '@prisma/client'; +import { match } from 'ts-pattern'; +import { z } from 'zod'; + +import { AppError, AppErrorCode } from '../errors/app-error'; + +const envelopeDocumentPrefixId = 'document'; +const envelopeTemplatePrefixId = 'template'; +const envelopePrefixId = 'envelope'; + +const ZDocumentIdSchema = z.string().regex(/^document_\d+$/); +const ZTemplateIdSchema = z.string().regex(/^template_\d+$/); +const ZEnvelopeIdSchema = z.string().regex(/^envelope_\d+$/); + +export type EnvelopeIdOptions = + | { + type: 'envelopeId'; + id: string; + } + | { + type: 'documentId'; + id: string | number; + } + | { + type: 'templateId'; + id: string | number; + }; + +/** + * Parses an unknown document or template ID. + * + * @param id + * @param type + * @returns + */ +export const buildEnvelopeIdQuery = (options: EnvelopeIdOptions) => { + return match(options) + .with({ type: 'envelopeId' }, (value) => { + const parsed = ZEnvelopeIdSchema.safeParse(value.id); + + if (!parsed.success) { + throw new AppError(AppErrorCode.INVALID_BODY, { + message: 'Invalid envelope ID', + }); + } + + return { + id: value.id, + }; + }) + .with({ type: 'documentId' }, (value) => ({ + type: EnvelopeType.DOCUMENT, + secondaryId: parseDocumentIdToEnvelopeSecondaryId(value.id), + })) + .with({ type: 'templateId' }, (value) => ({ + type: EnvelopeType.TEMPLATE, + secondaryId: parseTemplateIdToEnvelopeSecondaryId(value.id), + })) + .exhaustive(); +}; + +export const parseDocumentIdToEnvelopeSecondaryId = (documentId: string | number) => { + if (typeof documentId === 'number') { + return `${envelopeDocumentPrefixId}_${documentId}`; + } + + const parsed = ZDocumentIdSchema.safeParse(documentId); + + if (!parsed.success) { + throw new AppError(AppErrorCode.INVALID_BODY, { + message: 'Invalid document ID', + }); + } + + return parsed.data; +}; + +export const parseTemplateIdToEnvelopeSecondaryId = (templateId: string | number) => { + if (typeof templateId === 'number') { + return `${envelopeTemplatePrefixId}_${templateId}`; + } + + const parsed = ZTemplateIdSchema.safeParse(templateId); + + if (!parsed.success) { + throw new AppError(AppErrorCode.INVALID_BODY, { + message: 'Invalid template ID', + }); + } + + return parsed.data; +}; diff --git a/packages/lib/utils/templates.ts b/packages/lib/utils/templates.ts index edac17011..b11debda9 100644 --- a/packages/lib/utils/templates.ts +++ b/packages/lib/utils/templates.ts @@ -1,4 +1,4 @@ -import type { Recipient } from '@prisma/client'; +import { type Recipient } from '@prisma/client'; import { NEXT_PUBLIC_WEBAPP_URL } from '../constants/app'; diff --git a/packages/prisma/migrations/20250822050636_asdfasdf/migration.sql b/packages/prisma/migrations/20250822050636_asdfasdf/migration.sql new file mode 100644 index 000000000..de6a687d6 --- /dev/null +++ b/packages/prisma/migrations/20250822050636_asdfasdf/migration.sql @@ -0,0 +1,261 @@ +/* + Warnings: + + - You are about to drop the column `authOptions` on the `Document` table. All the data in the column will be lost. + - You are about to drop the column `completedAt` on the `Document` table. All the data in the column will be lost. + - You are about to drop the column `createdAt` on the `Document` table. All the data in the column will be lost. + - You are about to drop the column `deletedAt` on the `Document` table. All the data in the column will be lost. + - You are about to drop the column `externalId` on the `Document` table. All the data in the column will be lost. + - You are about to drop the column `folderId` on the `Document` table. All the data in the column will be lost. + - You are about to drop the column `formValues` on the `Document` table. All the data in the column will be lost. + - You are about to drop the column `source` on the `Document` table. All the data in the column will be lost. + - You are about to drop the column `status` on the `Document` table. All the data in the column will be lost. + - You are about to drop the column `teamId` on the `Document` table. All the data in the column will be lost. + - You are about to drop the column `templateId` on the `Document` table. All the data in the column will be lost. + - You are about to drop the column `updatedAt` on the `Document` table. All the data in the column will be lost. + - You are about to drop the column `useLegacyFieldInsertion` on the `Document` table. All the data in the column will be lost. + - You are about to drop the column `userId` on the `Document` table. All the data in the column will be lost. + - You are about to drop the column `visibility` on the `Document` table. All the data in the column will be lost. + - You are about to drop the column `documentId` on the `DocumentAuditLog` table. All the data in the column will be lost. + - You are about to drop the column `documentId` on the `DocumentMeta` table. All the data in the column will be lost. + - You are about to drop the column `templateId` on the `DocumentMeta` table. All the data in the column will be lost. + - You are about to drop the column `documentId` on the `DocumentShareLink` table. All the data in the column will be lost. + - You are about to drop the column `documentId` on the `Field` table. All the data in the column will be lost. + - You are about to drop the column `templateId` on the `Field` table. All the data in the column will be lost. + - You are about to drop the column `documentId` on the `Recipient` table. All the data in the column will be lost. + - You are about to drop the column `templateId` on the `Recipient` table. All the data in the column will be lost. + - You are about to drop the column `templateId` on the `TemplateDirectLink` table. All the data in the column will be lost. + - You are about to drop the `Template` table. If the table is not empty, all the data it contains will be lost. + - A unique constraint covering the columns `[envelopeId,email]` on the table `DocumentShareLink` will be added. If there are existing duplicate values, this will fail. + - A unique constraint covering the columns `[envelopeId,email]` on the table `Recipient` will be added. If there are existing duplicate values, this will fail. + - A unique constraint covering the columns `[envelopeId]` on the table `TemplateDirectLink` will be added. If there are existing duplicate values, this will fail. + - Added the required column `envelopeId` to the `DocumentAuditLog` table without a default value. This is not possible if the table is not empty. + - Added the required column `envelopeId` to the `DocumentShareLink` table without a default value. This is not possible if the table is not empty. + - Added the required column `envelopeId` to the `Field` table without a default value. This is not possible if the table is not empty. + - Added the required column `envelopeId` to the `Recipient` table without a default value. This is not possible if the table is not empty. + - Added the required column `envelopeId` to the `TemplateDirectLink` table without a default value. This is not possible if the table is not empty. + - Added the required column `type` to the `TemplateDirectLink` table without a default value. This is not possible if the table is not empty. + +*/ +-- CreateEnum +CREATE TYPE "EnvelopeType" AS ENUM ('DOCUMENT', 'TEMPLATE'); + +-- CreateEnum +CREATE TYPE "TemplateDirectLinkType" AS ENUM ('PUBLIC', 'PRIVATE'); + +-- DropForeignKey +ALTER TABLE "Document" DROP CONSTRAINT "Document_folderId_fkey"; + +-- DropForeignKey +ALTER TABLE "Document" DROP CONSTRAINT "Document_teamId_fkey"; + +-- DropForeignKey +ALTER TABLE "Document" DROP CONSTRAINT "Document_templateId_fkey"; + +-- DropForeignKey +ALTER TABLE "Document" DROP CONSTRAINT "Document_userId_fkey"; + +-- DropForeignKey +ALTER TABLE "DocumentAuditLog" DROP CONSTRAINT "DocumentAuditLog_documentId_fkey"; + +-- DropForeignKey +ALTER TABLE "DocumentMeta" DROP CONSTRAINT "DocumentMeta_documentId_fkey"; + +-- DropForeignKey +ALTER TABLE "DocumentMeta" DROP CONSTRAINT "DocumentMeta_templateId_fkey"; + +-- DropForeignKey +ALTER TABLE "DocumentShareLink" DROP CONSTRAINT "DocumentShareLink_documentId_fkey"; + +-- DropForeignKey +ALTER TABLE "Field" DROP CONSTRAINT "Field_documentId_fkey"; + +-- DropForeignKey +ALTER TABLE "Field" DROP CONSTRAINT "Field_templateId_fkey"; + +-- DropForeignKey +ALTER TABLE "Recipient" DROP CONSTRAINT "Recipient_documentId_fkey"; + +-- DropForeignKey +ALTER TABLE "Recipient" DROP CONSTRAINT "Recipient_templateId_fkey"; + +-- DropForeignKey +ALTER TABLE "Template" DROP CONSTRAINT "Template_folderId_fkey"; + +-- DropForeignKey +ALTER TABLE "Template" DROP CONSTRAINT "Template_teamId_fkey"; + +-- DropForeignKey +ALTER TABLE "Template" DROP CONSTRAINT "Template_templateDocumentDataId_fkey"; + +-- DropForeignKey +ALTER TABLE "Template" DROP CONSTRAINT "Template_userId_fkey"; + +-- DropForeignKey +ALTER TABLE "TemplateDirectLink" DROP CONSTRAINT "TemplateDirectLink_templateId_fkey"; + +-- DropIndex +DROP INDEX "Document_folderId_idx"; + +-- DropIndex +DROP INDEX "Document_status_idx"; + +-- DropIndex +DROP INDEX "Document_userId_idx"; + +-- DropIndex +DROP INDEX "DocumentMeta_documentId_key"; + +-- DropIndex +DROP INDEX "DocumentMeta_templateId_key"; + +-- DropIndex +DROP INDEX "DocumentShareLink_documentId_email_key"; + +-- DropIndex +DROP INDEX "Field_documentId_idx"; + +-- DropIndex +DROP INDEX "Field_templateId_idx"; + +-- DropIndex +DROP INDEX "Recipient_documentId_email_key"; + +-- DropIndex +DROP INDEX "Recipient_documentId_idx"; + +-- DropIndex +DROP INDEX "Recipient_templateId_email_key"; + +-- DropIndex +DROP INDEX "Recipient_templateId_idx"; + +-- DropIndex +DROP INDEX "TemplateDirectLink_templateId_key"; + +-- AlterTable +ALTER TABLE "Document" DROP COLUMN "authOptions", +DROP COLUMN "completedAt", +DROP COLUMN "createdAt", +DROP COLUMN "deletedAt", +DROP COLUMN "externalId", +DROP COLUMN "folderId", +DROP COLUMN "formValues", +DROP COLUMN "source", +DROP COLUMN "status", +DROP COLUMN "teamId", +DROP COLUMN "templateId", +DROP COLUMN "updatedAt", +DROP COLUMN "useLegacyFieldInsertion", +DROP COLUMN "userId", +DROP COLUMN "visibility"; + +-- AlterTable +ALTER TABLE "DocumentAuditLog" DROP COLUMN "documentId", +ADD COLUMN "envelopeId" TEXT NOT NULL; + +-- AlterTable +ALTER TABLE "DocumentMeta" DROP COLUMN "documentId", +DROP COLUMN "templateId", +ADD COLUMN "envelopeId" TEXT; + +-- AlterTable +ALTER TABLE "DocumentShareLink" DROP COLUMN "documentId", +ADD COLUMN "envelopeId" TEXT NOT NULL; + +-- AlterTable +ALTER TABLE "Field" DROP COLUMN "documentId", +DROP COLUMN "templateId", +ADD COLUMN "envelopeId" TEXT NOT NULL; + +-- AlterTable +ALTER TABLE "Recipient" DROP COLUMN "documentId", +DROP COLUMN "templateId", +ADD COLUMN "envelopeId" TEXT NOT NULL; + +-- AlterTable +ALTER TABLE "TemplateDirectLink" DROP COLUMN "templateId", +ADD COLUMN "envelopeId" TEXT NOT NULL, +ADD COLUMN "type" "TemplateDirectLinkType" NOT NULL; + +-- DropTable +DROP TABLE "Template"; + +-- DropEnum +DROP TYPE "TemplateType"; + +-- CreateTable +CREATE TABLE "Envelope" ( + "id" TEXT NOT NULL, + "secondaryId" TEXT NOT NULL, + "externalId" TEXT, + "type" "EnvelopeType" NOT NULL, + "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updatedAt" TIMESTAMP(3) NOT NULL, + "completedAt" TIMESTAMP(3), + "deletedAt" TIMESTAMP(3), + "title" TEXT NOT NULL, + "status" "DocumentStatus" NOT NULL DEFAULT 'DRAFT', + "source" "DocumentSource" NOT NULL, + "useLegacyFieldInsertion" BOOLEAN NOT NULL DEFAULT false, + "authOptions" JSONB, + "formValues" JSONB, + "visibility" "DocumentVisibility" NOT NULL DEFAULT 'EVERYONE', + "publicTitle" TEXT NOT NULL DEFAULT '', + "publicDescription" TEXT NOT NULL DEFAULT '', + "templateId" INTEGER, + "userId" INTEGER NOT NULL, + "teamId" INTEGER NOT NULL, + "folderId" TEXT, + "documentMetaId" TEXT NOT NULL, + + CONSTRAINT "Envelope_pkey" PRIMARY KEY ("id") +); + +-- CreateIndex +CREATE UNIQUE INDEX "Envelope_secondaryId_key" ON "Envelope"("secondaryId"); + +-- CreateIndex +CREATE UNIQUE INDEX "Envelope_documentMetaId_key" ON "Envelope"("documentMetaId"); + +-- CreateIndex +CREATE UNIQUE INDEX "DocumentShareLink_envelopeId_email_key" ON "DocumentShareLink"("envelopeId", "email"); + +-- CreateIndex +CREATE INDEX "Field_envelopeId_idx" ON "Field"("envelopeId"); + +-- CreateIndex +CREATE INDEX "Recipient_envelopeId_idx" ON "Recipient"("envelopeId"); + +-- CreateIndex +CREATE UNIQUE INDEX "Recipient_envelopeId_email_key" ON "Recipient"("envelopeId", "email"); + +-- CreateIndex +CREATE UNIQUE INDEX "TemplateDirectLink_envelopeId_key" ON "TemplateDirectLink"("envelopeId"); + +-- AddForeignKey +ALTER TABLE "Envelope" ADD CONSTRAINT "Envelope_userId_fkey" FOREIGN KEY ("userId") REFERENCES "User"("id") ON DELETE CASCADE ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "Envelope" ADD CONSTRAINT "Envelope_teamId_fkey" FOREIGN KEY ("teamId") REFERENCES "Team"("id") ON DELETE CASCADE ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "Envelope" ADD CONSTRAINT "Envelope_folderId_fkey" FOREIGN KEY ("folderId") REFERENCES "Folder"("id") ON DELETE SET NULL ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "Envelope" ADD CONSTRAINT "Envelope_documentMetaId_fkey" FOREIGN KEY ("documentMetaId") REFERENCES "DocumentMeta"("id") ON DELETE RESTRICT ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "DocumentAuditLog" ADD CONSTRAINT "DocumentAuditLog_envelopeId_fkey" FOREIGN KEY ("envelopeId") REFERENCES "Envelope"("id") ON DELETE CASCADE ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "Recipient" ADD CONSTRAINT "Recipient_envelopeId_fkey" FOREIGN KEY ("envelopeId") REFERENCES "Envelope"("id") ON DELETE CASCADE ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "Field" ADD CONSTRAINT "Field_envelopeId_fkey" FOREIGN KEY ("envelopeId") REFERENCES "Envelope"("id") ON DELETE CASCADE ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "DocumentShareLink" ADD CONSTRAINT "DocumentShareLink_envelopeId_fkey" FOREIGN KEY ("envelopeId") REFERENCES "Envelope"("id") ON DELETE CASCADE ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "TemplateDirectLink" ADD CONSTRAINT "TemplateDirectLink_envelopeId_fkey" FOREIGN KEY ("envelopeId") REFERENCES "Envelope"("id") ON DELETE CASCADE ON UPDATE CASCADE; diff --git a/packages/prisma/migrations/20250822065959_asdfasdf/migration.sql b/packages/prisma/migrations/20250822065959_asdfasdf/migration.sql new file mode 100644 index 000000000..ccdb95ffc --- /dev/null +++ b/packages/prisma/migrations/20250822065959_asdfasdf/migration.sql @@ -0,0 +1,11 @@ +/* + Warnings: + + - Added the required column `envelopeId` to the `Document` table without a default value. This is not possible if the table is not empty. + +*/ +-- AlterTable +ALTER TABLE "Document" ADD COLUMN "envelopeId" TEXT NOT NULL; + +-- AddForeignKey +ALTER TABLE "Document" ADD CONSTRAINT "Document_envelopeId_fkey" FOREIGN KEY ("envelopeId") REFERENCES "Envelope"("id") ON DELETE CASCADE ON UPDATE CASCADE; diff --git a/packages/prisma/schema.prisma b/packages/prisma/schema.prisma index b1c14d798..38634ca61 100644 --- a/packages/prisma/schema.prisma +++ b/packages/prisma/schema.prisma @@ -64,8 +64,7 @@ model User { twoFactorBackupCodes String? folders Folder[] - documents Document[] - templates Template[] + envelopes Envelope[] verificationTokens VerificationToken[] apiTokens ApiToken[] @@ -348,8 +347,7 @@ model Folder { pinned Boolean @default(false) parentId String? parent Folder? @relation("FolderToFolder", fields: [parentId], references: [id], onDelete: Cascade) - documents Document[] - templates Template[] + envelopes Envelope[] createdAt DateTime @default(now()) updatedAt DateTime @default(now()) @updatedAt subfolders Folder[] @relation("FolderToFolder") @@ -362,53 +360,79 @@ model Folder { @@index([type]) } -/// @zod.import(["import { ZDocumentAuthOptionsSchema } from '@documenso/lib/types/document-auth';", "import { ZDocumentFormValuesSchema } from '@documenso/lib/types/document-form-values';"]) -model Document { - id Int @id @default(autoincrement()) - qrToken String? /// @zod.string.describe("The token for viewing the document using the QR code on the certificate.") - externalId String? /// @zod.string.describe("A custom external ID you can use to identify the document.") +enum EnvelopeType { + DOCUMENT + TEMPLATE +} +model Envelope { + id String @id @default(cuid()) + secondaryId String @unique + externalId String? /// @zod.string.describe("A custom external ID you can use to identify the document.") + + type EnvelopeType + + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt + completedAt DateTime? + deletedAt DateTime? + + title String + status DocumentStatus @default(DRAFT) + source DocumentSource + + useLegacyFieldInsertion Boolean @default(false) + + documents Document[] + recipients Recipient[] + fields Field[] + shareLinks DocumentShareLink[] + auditLogs DocumentAuditLog[] + + // Envelope settings + authOptions Json? /// [DocumentAuthOptions] @zod.custom.use(ZDocumentAuthOptionsSchema) + formValues Json? /// [DocumentFormValues] @zod.custom.use(ZDocumentFormValuesSchema) + visibility DocumentVisibility @default(EVERYONE) + + // Template specific fields. + publicTitle String @default("") + publicDescription String @default("") + directLink TemplateDirectLink? + templateId Int? // Todo: Migrate from templateId -> This @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ + + // Relations userId Int /// @zod.number.describe("The ID of the user that created this document.") user User @relation(fields: [userId], references: [id], onDelete: Cascade) teamId Int team Team @relation(fields: [teamId], references: [id], onDelete: Cascade) - authOptions Json? /// [DocumentAuthOptions] @zod.custom.use(ZDocumentAuthOptionsSchema) - formValues Json? /// [DocumentFormValues] @zod.custom.use(ZDocumentFormValuesSchema) - visibility DocumentVisibility @default(EVERYONE) - title String - status DocumentStatus @default(DRAFT) - recipients Recipient[] - fields Field[] - shareLinks DocumentShareLink[] + folder Folder? @relation(fields: [folderId], references: [id], onDelete: SetNull) + folderId String? + + documentMetaId String @unique + documentMeta DocumentMeta @relation(fields: [documentMetaId], references: [id]) +} + +/// @zod.import(["import { ZDocumentAuthOptionsSchema } from '@documenso/lib/types/document-auth';", "import { ZDocumentFormValuesSchema } from '@documenso/lib/types/document-form-values';"]) +model Document { + id Int @id @default(autoincrement()) + + qrToken String? /// @zod.string.describe("The token for viewing the document using the QR code on the certificate.") + title String + documentDataId String - documentMeta DocumentMeta? - createdAt DateTime @default(now()) - updatedAt DateTime @default(now()) @updatedAt - completedAt DateTime? - deletedAt DateTime? - templateId Int? - source DocumentSource + documentData DocumentData @relation(fields: [documentDataId], references: [id], onDelete: Cascade) - useLegacyFieldInsertion Boolean @default(false) - - documentData DocumentData @relation(fields: [documentDataId], references: [id], onDelete: Cascade) - template Template? @relation(fields: [templateId], references: [id], onDelete: SetNull) - - auditLogs DocumentAuditLog[] - folder Folder? @relation(fields: [folderId], references: [id], onDelete: SetNull) - folderId String? + envelopeId String + envelope Envelope @relation(fields: [envelopeId], references: [id], onDelete: Cascade) @@unique([documentDataId]) - @@index([userId]) - @@index([status]) - @@index([folderId]) } model DocumentAuditLog { id String @id @default(cuid()) - documentId Int + envelopeId String createdAt DateTime @default(now()) type String data Json @@ -420,7 +444,7 @@ model DocumentAuditLog { userAgent String? ipAddress String? - Document Document @relation(fields: [documentId], references: [id], onDelete: Cascade) + envelope Envelope @relation(fields: [envelopeId], references: [id], onDelete: Cascade) } enum DocumentDataType { @@ -440,7 +464,6 @@ model DocumentData { data String initialData String document Document? - template Template? } enum DocumentDistributionMethod { @@ -471,11 +494,8 @@ model DocumentMeta { emailReplyTo String? emailId String? - documentId Int? @unique - document Document? @relation(fields: [documentId], references: [id], onDelete: Cascade) - - templateId Int? @unique - template Template? @relation(fields: [templateId], references: [id], onDelete: Cascade) + envelopeId String? + envelope Envelope? } enum ReadStatus { @@ -505,8 +525,7 @@ enum RecipientRole { /// @zod.import(["import { ZRecipientAuthOptionsSchema } from '@documenso/lib/types/document-auth';"]) model Recipient { id Int @id @default(autoincrement()) - documentId Int? - templateId Int? + envelopeId String email String @db.VarChar(255) name String @default("") @db.VarChar(255) token String @@ -520,15 +539,12 @@ model Recipient { readStatus ReadStatus @default(NOT_OPENED) signingStatus SigningStatus @default(NOT_SIGNED) sendStatus SendStatus @default(NOT_SENT) - document Document? @relation(fields: [documentId], references: [id], onDelete: Cascade) - template Template? @relation(fields: [templateId], references: [id], onDelete: Cascade) + envelope Envelope @relation(fields: [envelopeId], references: [id], onDelete: Cascade) fields Field[] signatures Signature[] - @@unique([documentId, email]) - @@unique([templateId, email]) - @@index([documentId]) - @@index([templateId]) + @@unique([envelopeId, email]) + @@index([envelopeId]) @@index([token]) } @@ -550,8 +566,7 @@ enum FieldType { model Field { id Int @id @default(autoincrement()) secondaryId String @unique @default(cuid()) - documentId Int? - templateId Int? + envelopeId String recipientId Int type FieldType page Int /// @zod.number.describe("The page number of the field on the document. Starts from 1.") @@ -561,14 +576,12 @@ model Field { height Decimal @default(-1) customText String inserted Boolean - document Document? @relation(fields: [documentId], references: [id], onDelete: Cascade) - template Template? @relation(fields: [templateId], references: [id], onDelete: Cascade) + envelope Envelope @relation(fields: [envelopeId], references: [id], onDelete: Cascade) recipient Recipient @relation(fields: [recipientId], references: [id], onDelete: Cascade) signature Signature? fieldMeta Json? /// [FieldMeta] @zod.custom.use(ZFieldMetaNotOptionalSchema) - @@index([documentId]) - @@index([templateId]) + @@index([envelopeId]) @@index([recipientId]) } @@ -590,13 +603,13 @@ model DocumentShareLink { id Int @id @default(autoincrement()) email String slug String @unique - documentId Int + envelopeId String createdAt DateTime @default(now()) updatedAt DateTime @updatedAt - document Document @relation(fields: [documentId], references: [id], onDelete: Cascade) + envelope Envelope @relation(fields: [envelopeId], references: [id], onDelete: Cascade) - @@unique([documentId, email]) + @@unique([envelopeId, email]) } enum OrganisationType { @@ -807,8 +820,7 @@ model Team { profile TeamProfile? - documents Document[] - templates Template[] + envelopes Envelope[] folders Folder[] apiTokens ApiToken[] webhooks Webhook[] @@ -841,58 +853,26 @@ model TeamEmailVerification { team Team @relation(fields: [teamId], references: [id], onDelete: Cascade) } -enum TemplateType { +// TODO: USE THIS +// TODO: USE THIS +// TODO: USE THIS +// TODO: USE THIS +enum TemplateDirectLinkType { PUBLIC PRIVATE } -/// @zod.import(["import { ZDocumentAuthOptionsSchema } from '@documenso/lib/types/document-auth';"]) -model Template { - id Int @id @default(autoincrement()) - externalId String? - type TemplateType @default(PRIVATE) - title String - visibility DocumentVisibility @default(EVERYONE) - authOptions Json? /// [DocumentAuthOptions] @zod.custom.use(ZDocumentAuthOptionsSchema) - templateMeta DocumentMeta? - templateDocumentDataId String - createdAt DateTime @default(now()) - updatedAt DateTime @default(now()) @updatedAt - publicTitle String @default("") - publicDescription String @default("") - - useLegacyFieldInsertion Boolean @default(false) - - userId Int - user User @relation(fields: [userId], references: [id], onDelete: Cascade) - - teamId Int - team Team @relation(fields: [teamId], references: [id], onDelete: Cascade) - - templateDocumentData DocumentData @relation(fields: [templateDocumentDataId], references: [id], onDelete: Cascade) - - recipients Recipient[] - fields Field[] - directLink TemplateDirectLink? - documents Document[] - - folder Folder? @relation(fields: [folderId], references: [id], onDelete: SetNull) - folderId String? - - @@unique([templateDocumentDataId]) - @@index([userId]) -} - model TemplateDirectLink { - id String @id @unique @default(cuid()) - templateId Int @unique - token String @unique - createdAt DateTime @default(now()) + id String @id @unique @default(cuid()) + envelopeId String @unique + token String @unique + createdAt DateTime @default(now()) enabled Boolean + type TemplateDirectLinkType directTemplateRecipientId Int - template Template @relation(fields: [templateId], references: [id], onDelete: Cascade) + envelope Envelope @relation(fields: [envelopeId], references: [id], onDelete: Cascade) } model SiteSettings { diff --git a/packages/prisma/types/document-legacy-schema.ts b/packages/prisma/types/document-legacy-schema.ts new file mode 100644 index 000000000..e3c71ad1d --- /dev/null +++ b/packages/prisma/types/document-legacy-schema.ts @@ -0,0 +1,59 @@ +/** + * Legacy Document schema to confirm backwards API compatibility since + * we migrated Documents to Envelopes. + */ +import { z } from 'zod'; + +import { ZDocumentAuthOptionsSchema } from '@documenso/lib/types/document-auth'; +import { ZDocumentFormValuesSchema } from '@documenso/lib/types/document-form-values'; + +import DocumentStatusSchema from '../generated/zod/inputTypeSchemas/DocumentStatusSchema'; +import DocumentVisibilitySchema from '../generated/zod/inputTypeSchemas/DocumentVisibilitySchema'; + +const DocumentSourceSchema = z.enum(['DOCUMENT', 'TEMPLATE', 'TEMPLATE_DIRECT_LINK']); +const DocumentTypeSchema = z.enum(['DOCUMENT', 'PUBLIC_TEMPLATE', 'PRIVATE_TEMPLATE']); + +///////////////////////////////////////// +// DOCUMENT SCHEMA +///////////////////////////////////////// + +export const LegacyDocumentSchema = z.object({ + type: DocumentTypeSchema, + visibility: DocumentVisibilitySchema, + status: DocumentStatusSchema, + source: DocumentSourceSchema, + id: z.number(), + qrToken: z + .string() + .describe('The token for viewing the document using the QR code on the certificate.') + .nullable(), + externalId: z + .string() + .describe('A custom external ID you can use to identify the document.') + .nullable(), + secondaryDocumentId: z.number(), + secondaryTemplateId: z.number(), + publicTitle: z.string(), + publicDescription: z.string(), + createdFromDocumentId: z.number().nullable(), + userId: z.number().describe('The ID of the user that created this document.'), + teamId: z.number(), + /** + * [DocumentAuthOptions] + */ + authOptions: ZDocumentAuthOptionsSchema.nullable(), + /** + * [DocumentFormValues] + */ + formValues: ZDocumentFormValuesSchema.nullable(), + title: z.string(), + documentDataId: z.string(), + createdAt: z.coerce.date(), + updatedAt: z.coerce.date(), + completedAt: z.coerce.date().nullable(), + deletedAt: z.coerce.date().nullable(), + useLegacyFieldInsertion: z.boolean(), + folderId: z.string().nullable(), +}); + +export type Document = z.infer; diff --git a/packages/prisma/types/template-legacy-schema.ts b/packages/prisma/types/template-legacy-schema.ts new file mode 100644 index 000000000..85ff6b886 --- /dev/null +++ b/packages/prisma/types/template-legacy-schema.ts @@ -0,0 +1,36 @@ +/** + * Legacy Template schema to confirm backwards API compatibility since + * we removed the "Template" prisma schema model. + */ +import { z } from 'zod'; + +import { ZDocumentAuthOptionsSchema } from '@documenso/lib/types/document-auth'; + +import { DocumentVisibilitySchema } from '../generated/zod/inputTypeSchemas/DocumentVisibilitySchema'; + +export const TemplateTypeSchema = z.enum(['PUBLIC', 'PRIVATE']); + +export type TemplateTypeType = `${z.infer}`; + +export const TemplateSchema = z.object({ + type: TemplateTypeSchema, + visibility: DocumentVisibilitySchema, + id: z.number(), + externalId: z.string().nullable(), + title: z.string(), + /** + * [DocumentAuthOptions] + */ + authOptions: ZDocumentAuthOptionsSchema.nullable(), + templateDocumentDataId: z.string(), + createdAt: z.coerce.date(), + updatedAt: z.coerce.date(), + publicTitle: z.string(), + publicDescription: z.string(), + useLegacyFieldInsertion: z.boolean(), + userId: z.number(), + teamId: z.number(), + folderId: z.string().nullable(), +}); + +export type Template = z.infer;