diff --git a/apps/web/src/app/(unauthenticated)/articles/signature-disclosure/page.tsx b/apps/web/src/app/(unauthenticated)/articles/signature-disclosure/page.tsx
index 878332f35..c56f53702 100644
--- a/apps/web/src/app/(unauthenticated)/articles/signature-disclosure/page.tsx
+++ b/apps/web/src/app/(unauthenticated)/articles/signature-disclosure/page.tsx
@@ -5,7 +5,7 @@ import { Button } from '@documenso/ui/primitives/button';
export default function SignatureDisclosure() {
return (
-
+
Electronic Signature Disclosure
Welcome
diff --git a/apps/web/src/pages/api/auth/[...nextauth].ts b/apps/web/src/pages/api/auth/[...nextauth].ts
index 365b6ec40..31f6e9ea3 100644
--- a/apps/web/src/pages/api/auth/[...nextauth].ts
+++ b/apps/web/src/pages/api/auth/[...nextauth].ts
@@ -2,6 +2,8 @@ import type { NextApiRequest, NextApiResponse } from 'next';
import NextAuth from 'next-auth';
+import { getStripeCustomerByUser } from '@documenso/ee/server-only/stripe/get-customer';
+import { IS_BILLING_ENABLED } from '@documenso/lib/constants/app';
import { NEXT_AUTH_OPTIONS } from '@documenso/lib/next-auth/auth-options';
import { extractNextApiRequestMetadata } from '@documenso/lib/universal/extract-request-metadata';
import { prisma } from '@documenso/prisma';
@@ -18,15 +20,29 @@ export default async function auth(req: NextApiRequest, res: NextApiResponse) {
error: '/signin',
},
events: {
- signIn: async ({ user }) => {
- await prisma.userSecurityAuditLog.create({
- data: {
- userId: user.id,
- ipAddress,
- userAgent,
- type: UserSecurityAuditLogType.SIGN_IN,
- },
- });
+ signIn: async ({ user: { id: userId } }) => {
+ const [user] = await Promise.all([
+ await prisma.user.findFirstOrThrow({
+ where: {
+ id: userId,
+ },
+ }),
+ await prisma.userSecurityAuditLog.create({
+ data: {
+ userId,
+ ipAddress,
+ userAgent,
+ type: UserSecurityAuditLogType.SIGN_IN,
+ },
+ }),
+ ]);
+
+ // Create the Stripe customer and attach it to the user if it doesn't exist.
+ if (user.customerId === null && IS_BILLING_ENABLED()) {
+ await getStripeCustomerByUser(user).catch((err) => {
+ console.error(err);
+ });
+ }
},
signOut: async ({ token }) => {
const userId = typeof token.id === 'string' ? parseInt(token.id) : token.id;
diff --git a/packages/lib/server-only/document/complete-document-with-token.ts b/packages/lib/server-only/document/complete-document-with-token.ts
index d16b83ea1..29d17cc50 100644
--- a/packages/lib/server-only/document/complete-document-with-token.ts
+++ b/packages/lib/server-only/document/complete-document-with-token.ts
@@ -137,7 +137,7 @@ export const completeDocumentWithToken = async ({
await sendPendingEmail({ documentId, recipientId: recipient.id });
}
- const documents = await prisma.document.updateMany({
+ const haveAllRecipientsSigned = await prisma.document.findFirst({
where: {
id: document.id,
Recipient: {
@@ -146,13 +146,9 @@ export const completeDocumentWithToken = async ({
},
},
},
- data: {
- status: DocumentStatus.COMPLETED,
- completedAt: new Date(),
- },
});
- if (documents.count > 0) {
+ if (haveAllRecipientsSigned) {
await sealDocument({ documentId: document.id, requestMetadata });
}
diff --git a/packages/lib/server-only/document/delete-document.ts b/packages/lib/server-only/document/delete-document.ts
index a097d76e9..bf4f8aa06 100644
--- a/packages/lib/server-only/document/delete-document.ts
+++ b/packages/lib/server-only/document/delete-document.ts
@@ -75,18 +75,20 @@ export const deleteDocument = async ({
}
// Continue to hide the document from the user if they are a recipient.
+ // Dirty way of doing this but it's faster than refetching the document.
if (userRecipient?.documentDeletedAt === null) {
- await prisma.recipient.update({
- where: {
- documentId_email: {
- documentId: document.id,
- email: user.email,
+ await prisma.recipient
+ .update({
+ where: {
+ id: userRecipient.id,
},
- },
- data: {
- documentDeletedAt: new Date().toISOString(),
- },
- });
+ data: {
+ documentDeletedAt: new Date().toISOString(),
+ },
+ })
+ .catch(() => {
+ // Do nothing.
+ });
}
// Return partial document for API v1 response.
diff --git a/packages/lib/server-only/document/seal-document.ts b/packages/lib/server-only/document/seal-document.ts
index 3e366dc81..564dfc049 100644
--- a/packages/lib/server-only/document/seal-document.ts
+++ b/packages/lib/server-only/document/seal-document.ts
@@ -40,6 +40,11 @@ export const sealDocument = async ({
const document = await prisma.document.findFirstOrThrow({
where: {
id: documentId,
+ Recipient: {
+ every: {
+ signingStatus: SigningStatus.SIGNED,
+ },
+ },
},
include: {
documentData: true,
@@ -53,10 +58,6 @@ export const sealDocument = async ({
throw new Error(`Document ${document.id} has no document data`);
}
- if (document.status !== DocumentStatus.COMPLETED) {
- throw new Error(`Document ${document.id} has not been completed`);
- }
-
const recipients = await prisma.recipient.findMany({
where: {
documentId: document.id,
@@ -92,9 +93,9 @@ export const sealDocument = async ({
// !: Need to write the fields onto the document as a hard copy
const pdfData = await getFile(documentData);
- const certificate = await getCertificatePdf({ documentId }).then(async (doc) =>
- PDFDocument.load(doc),
- );
+ const certificate = await getCertificatePdf({ documentId })
+ .then(async (doc) => PDFDocument.load(doc))
+ .catch(() => null);
const doc = await PDFDocument.load(pdfData);
@@ -103,11 +104,13 @@ export const sealDocument = async ({
doc.getForm().flatten();
flattenAnnotations(doc);
- const certificatePages = await doc.copyPages(certificate, certificate.getPageIndices());
+ if (certificate) {
+ const certificatePages = await doc.copyPages(certificate, certificate.getPageIndices());
- certificatePages.forEach((page) => {
- doc.addPage(page);
- });
+ certificatePages.forEach((page) => {
+ doc.addPage(page);
+ });
+ }
for (const field of fields) {
await insertFieldInPDF(doc, field);
@@ -138,6 +141,16 @@ export const sealDocument = async ({
}
await prisma.$transaction(async (tx) => {
+ await tx.document.update({
+ where: {
+ id: document.id,
+ },
+ data: {
+ status: DocumentStatus.COMPLETED,
+ completedAt: new Date(),
+ },
+ });
+
await tx.documentData.update({
where: {
id: documentData.id,
diff --git a/packages/lib/server-only/htmltopdf/get-certificate-pdf.ts b/packages/lib/server-only/htmltopdf/get-certificate-pdf.ts
index dee40d41a..1b6150fb9 100644
--- a/packages/lib/server-only/htmltopdf/get-certificate-pdf.ts
+++ b/packages/lib/server-only/htmltopdf/get-certificate-pdf.ts
@@ -35,6 +35,7 @@ export const getCertificatePdf = async ({ documentId }: GetCertificatePdfOptions
await page.goto(`${NEXT_PUBLIC_WEBAPP_URL()}/__htmltopdf/certificate?d=${encryptedId}`, {
waitUntil: 'networkidle',
+ timeout: 10_000,
});
const result = await page.pdf({
diff --git a/packages/ui/primitives/template-flow/add-template-placeholder-recipients.tsx b/packages/ui/primitives/template-flow/add-template-placeholder-recipients.tsx
index e415f1aac..d285fbe44 100644
--- a/packages/ui/primitives/template-flow/add-template-placeholder-recipients.tsx
+++ b/packages/ui/primitives/template-flow/add-template-placeholder-recipients.tsx
@@ -282,6 +282,7 @@ export const AddTemplatePlaceholderRecipientsFormPartial = ({