Merge branch 'feat/refresh' into v2-google-auth

This commit is contained in:
Mythie
2023-07-31 13:03:32 +10:00
118 changed files with 17723 additions and 612 deletions

View File

@ -0,0 +1,6 @@
export const initials = (text: string) =>
text
?.split(' ')
.map((name: string) => name.slice(0, 1).toUpperCase())
.slice(0, 2)
.join('') ?? 'UK';

View File

@ -0,0 +1,13 @@
import { Recipient } from '@documenso/prisma/client';
export const getRecipientType = (recipient: Recipient) => {
if (recipient.sendStatus === 'SENT' && recipient.signingStatus === 'SIGNED') {
return 'completed';
}
if (recipient.sendStatus === 'SENT' && recipient.signingStatus === 'NOT_SIGNED') {
return 'waiting';
}
return 'unsigned';
};

View File

@ -31,7 +31,6 @@ export const NEXT_AUTH_OPTIONS: AuthOptions = {
const user = await getUserByEmail({ email }).catch(() => null);
if (!user || !user.password) {
console.log('no user');
return null;
}

View File

@ -13,14 +13,17 @@
"scripts": {
},
"dependencies": {
"@documenso/email": "*",
"@documenso/prisma": "*",
"@pdf-lib/fontkit": "^1.1.1",
"@next-auth/prisma-adapter": "^1.0.6",
"@upstash/redis": "^1.20.6",
"bcrypt": "^5.1.0",
"pdf-lib": "^1.17.1",
"next": "13.4.1",
"nanoid": "^4.0.2",
"next": "13.4.9",
"next-auth": "^4.22.1",
"react": "18.2.0",
"stripe": "^12.7.0"
},
"devDependencies": {

View File

@ -1,5 +1,6 @@
import { prisma } from '@documenso/prisma';
import { Document, DocumentStatus, Prisma } from '@documenso/prisma/client';
import { DocumentWithReciepient } from '@documenso/prisma/types/document-with-recipient';
import { FindResultSet } from '../../types/find-result-set';
@ -22,7 +23,7 @@ export const findDocuments = async ({
page = 1,
perPage = 10,
orderBy,
}: FindDocumentsOptions): Promise<FindResultSet<Document>> => {
}: FindDocumentsOptions): Promise<FindResultSet<DocumentWithReciepient>> => {
const orderByColumn = orderBy?.column ?? 'created';
const orderByDirection = orderBy?.direction ?? 'desc';
@ -48,6 +49,9 @@ export const findDocuments = async ({
orderBy: {
[orderByColumn]: orderByDirection,
},
include: {
Recipient: true,
},
}),
prisma.document.count({
where: {

View File

@ -0,0 +1,94 @@
import { createElement } from 'react';
import { mailer } from '@documenso/email/mailer';
import { render } from '@documenso/email/render';
import { DocumentInviteEmailTemplate } from '@documenso/email/templates/document-invite';
import { prisma } from '@documenso/prisma';
import { DocumentStatus, SendStatus } from '@documenso/prisma/client';
export interface SendDocumentOptions {
documentId: number;
userId: number;
}
export const sendDocument = async ({ documentId, userId }: SendDocumentOptions) => {
const user = await prisma.user.findFirstOrThrow({
where: {
id: userId,
},
});
const document = await prisma.document.findUnique({
where: {
id: documentId,
userId,
},
include: {
Recipient: true,
},
});
if (!document) {
throw new Error('Document not found');
}
if (document.Recipient.length === 0) {
throw new Error('Document has no recipients');
}
if (document.status === DocumentStatus.COMPLETED) {
throw new Error('Can not send completed document');
}
await Promise.all([
document.Recipient.map(async (recipient) => {
const { email, name } = recipient;
if (recipient.sendStatus === SendStatus.SENT) {
return;
}
const template = createElement(DocumentInviteEmailTemplate, {
documentName: document.title,
assetBaseUrl: process.env.NEXT_PUBLIC_SITE_URL || 'http://localhost:3000',
inviterName: user.name || undefined,
inviterEmail: user.email,
signDocumentLink: 'https://example.com',
});
mailer.sendMail({
to: {
address: email,
name,
},
from: {
name: process.env.NEXT_PRIVATE_SMTP_FROM_NAME || 'Documenso',
address: process.env.NEXT_PRIVATE_SMTP_FROM_ADDRESS || 'noreply@documenso.com',
},
subject: 'Please sign this document',
html: render(template),
text: render(template, { plainText: true }),
});
await prisma.recipient.update({
where: {
id: recipient.id,
},
data: {
sendStatus: SendStatus.SENT,
},
});
}),
]);
const updatedDocument = await prisma.document.update({
where: {
id: documentId,
},
data: {
status: DocumentStatus.PENDING,
},
});
return updatedDocument;
};

View File

@ -0,0 +1,19 @@
import { prisma } from '@documenso/prisma';
export interface GetFieldsForDocumentOptions {
documentId: number;
userId: number;
}
export const getFieldsForDocument = async ({ documentId, userId }: GetFieldsForDocumentOptions) => {
const fields = await prisma.field.findMany({
where: {
documentId,
Document: {
userId,
},
},
});
return fields;
};

View File

@ -0,0 +1,125 @@
import { prisma } from '@documenso/prisma';
import { SendStatus, SigningStatus } from '@documenso/prisma/client';
export interface SetFieldsForDocumentOptions {
userId: number;
documentId: number;
fields: {
id?: number | null;
signerEmail: string;
pageNumber: number;
pageX: number;
pageY: number;
pageWidth: number;
pageHeight: number;
}[];
}
export const setFieldsForDocument = async ({
userId,
documentId,
fields,
}: SetFieldsForDocumentOptions) => {
const document = await prisma.document.findFirst({
where: {
id: documentId,
userId,
},
});
if (!document) {
throw new Error('Document not found');
}
const existingFields = await prisma.field.findMany({
where: {
documentId,
},
include: {
Recipient: true,
},
});
const removedFields = existingFields.filter(
(existingField) =>
!fields.find(
(field) =>
field.id === existingField.id || field.signerEmail === existingField.Recipient?.email,
),
);
const linkedFields = fields
.map((field) => {
const existing = existingFields.find((existingField) => existingField.id === field.id);
return {
...field,
...existing,
};
})
.filter((field) => {
return (
field.Recipient?.sendStatus !== SendStatus.SENT &&
field.Recipient?.signingStatus !== SigningStatus.SIGNED
);
});
const persistedFields = await prisma.$transaction(
linkedFields.map((field) =>
field.id
? prisma.field.update({
where: {
id: field.id,
recipientId: field.recipientId,
documentId,
},
data: {
type: field.type,
page: field.pageNumber,
positionX: field.pageX,
positionY: field.pageY,
width: field.pageWidth,
height: field.pageHeight,
},
})
: prisma.field.create({
data: {
type: field.type!,
page: field.pageNumber,
positionX: field.pageX,
positionY: field.pageY,
width: field.pageWidth,
height: field.pageHeight,
customText: '',
inserted: false,
Document: {
connect: {
id: document.id,
},
},
Recipient: {
connect: {
documentId_email: {
documentId: document.id,
email: field.signerEmail,
},
},
},
},
}),
),
);
if (removedFields.length > 0) {
await prisma.field.deleteMany({
where: {
id: {
in: removedFields.map((field) => field.id),
},
},
});
}
return persistedFields;
};

View File

@ -0,0 +1,22 @@
import { prisma } from '@documenso/prisma';
export interface GetRecipientsForDocumentOptions {
documentId: number;
userId: number;
}
export const getRecipientsForDocument = async ({
documentId,
userId,
}: GetRecipientsForDocumentOptions) => {
const recipients = await prisma.recipient.findMany({
where: {
documentId,
Document: {
userId,
},
},
});
return recipients;
};

View File

@ -0,0 +1,100 @@
import { nanoid } from 'nanoid';
import { prisma } from '@documenso/prisma';
import { SendStatus, SigningStatus } from '@documenso/prisma/client';
export interface SetRecipientsForDocumentOptions {
userId: number;
documentId: number;
recipients: {
id?: number | null;
email: string;
name: string;
}[];
}
export const setRecipientsForDocument = async ({
userId,
documentId,
recipients,
}: SetRecipientsForDocumentOptions) => {
const document = await prisma.document.findFirst({
where: {
id: documentId,
userId,
},
});
if (!document) {
throw new Error('Document not found');
}
const existingRecipients = await prisma.recipient.findMany({
where: {
documentId,
},
});
const removedRecipients = existingRecipients.filter(
(existingRecipient) =>
!recipients.find(
(recipient) =>
recipient.id === existingRecipient.id || recipient.email === existingRecipient.email,
),
);
const linkedRecipients = recipients
.map((recipient) => {
const existing = existingRecipients.find(
(existingRecipient) =>
existingRecipient.id === recipient.id || existingRecipient.email === recipient.email,
);
return {
...recipient,
...existing,
};
})
.filter((recipient) => {
return (
recipient.sendStatus !== SendStatus.SENT && recipient.signingStatus !== SigningStatus.SIGNED
);
});
const persistedRecipients = await prisma.$transaction(
linkedRecipients.map((recipient) =>
recipient.id
? prisma.recipient.update({
where: {
id: recipient.id,
documentId,
},
data: {
name: recipient.name,
email: recipient.email,
documentId,
},
})
: prisma.recipient.create({
data: {
name: recipient.name,
email: recipient.email,
token: nanoid(),
documentId,
},
}),
),
);
if (removedRecipients.length > 0) {
await prisma.recipient.deleteMany({
where: {
id: {
in: removedRecipients.map((recipient) => recipient.id),
},
},
});
}
return persistedRecipients;
};