mirror of
https://github.com/documenso/documenso.git
synced 2025-11-14 00:32:43 +10:00
Merge branch 'feat/refresh' into v2-google-auth
This commit is contained in:
6
packages/lib/client-only/recipient-initials.ts
Normal file
6
packages/lib/client-only/recipient-initials.ts
Normal 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';
|
||||
13
packages/lib/client-only/recipient-type.ts
Normal file
13
packages/lib/client-only/recipient-type.ts
Normal 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';
|
||||
};
|
||||
@ -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;
|
||||
}
|
||||
|
||||
|
||||
@ -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": {
|
||||
|
||||
@ -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: {
|
||||
|
||||
94
packages/lib/server-only/document/send-document.tsx
Normal file
94
packages/lib/server-only/document/send-document.tsx
Normal 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;
|
||||
};
|
||||
19
packages/lib/server-only/field/get-fields-for-document.ts
Normal file
19
packages/lib/server-only/field/get-fields-for-document.ts
Normal 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;
|
||||
};
|
||||
125
packages/lib/server-only/field/set-fields-for-document.ts
Normal file
125
packages/lib/server-only/field/set-fields-for-document.ts
Normal 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;
|
||||
};
|
||||
@ -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;
|
||||
};
|
||||
@ -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;
|
||||
};
|
||||
Reference in New Issue
Block a user