mirror of
https://github.com/documenso/documenso.git
synced 2025-11-13 08:13:56 +10:00
Merge branch 'main' into feat/document-auth
This commit is contained in:
13
README.md
13
README.md
@ -30,17 +30,8 @@
|
||||
<a href="CODE_OF_CONDUCT.md"><img src="https://img.shields.io/badge/Contributor%20Covenant-2.1-4baaaa.svg" alt="Contributor Covenant"></a>
|
||||
</p>
|
||||
|
||||
<div>
|
||||
<img style="display: block; height: 120px; width: 24%"
|
||||
src="https://github.com/documenso/documenso/assets/1309312/67e08c98-c153-4115-aa2d-77979bb12c94)">
|
||||
<img style="display: block; height: 120px; width: 24%"
|
||||
src="https://github.com/documenso/documenso/assets/1309312/040cfbae-3438-4ca3-87f2-ce52c793dcaf">
|
||||
<img style="display: block; height: 120px; width: 24%"
|
||||
src="https://github.com/documenso/documenso/assets/1309312/72d445be-41e5-4936-bdba-87ef8e70fa09">
|
||||
<img style="display: block; height: 120px; width: 24%"
|
||||
src="https://github.com/documenso/documenso/assets/1309312/d7b86c0f-a755-4476-a022-a608db2c4633">
|
||||
<img style="display: block; height: 120px; width: 24%"
|
||||
src=https://github.com/documenso/documenso/assets/1309312/c0f55116-ab82-433f-a266-f3fc8571d69f">
|
||||
<div align="center">
|
||||
<img src="https://github.com/documenso/documenso/assets/13398220/d96ed533-6f34-4a97-be9b-442bdb189c69" style="width: 80%;" />
|
||||
</div>
|
||||
|
||||
## About this project
|
||||
|
||||
@ -93,7 +93,7 @@ export const MenuSwitcher = ({ user, teams: initialTeamsData }: MenuSwitcherProp
|
||||
<Button
|
||||
data-testid="menu-switcher"
|
||||
variant="none"
|
||||
className="relative flex h-12 flex-row items-center px-2 py-2 ring-0 focus-visible:border-0 focus-visible:ring-0"
|
||||
className="relative flex h-12 flex-row items-center px-2 py-2 ring-0 focus:outline-none focus-visible:border-0 focus-visible:ring-0 focus-visible:ring-transparent"
|
||||
>
|
||||
<AvatarWithText
|
||||
avatarFallback={formatAvatarFallback(selectedTeam?.name)}
|
||||
|
||||
@ -1,11 +1,30 @@
|
||||
'use client';
|
||||
|
||||
import { useEffect } from 'react';
|
||||
|
||||
import { useTheme } from 'next-themes';
|
||||
import SwaggerUI from 'swagger-ui-react';
|
||||
import 'swagger-ui-react/swagger-ui.css';
|
||||
|
||||
import { OpenAPIV1 } from '@documenso/api/v1/openapi';
|
||||
|
||||
export const OpenApiDocsPage = () => {
|
||||
const { resolvedTheme } = useTheme();
|
||||
|
||||
useEffect(() => {
|
||||
const body = document.body;
|
||||
|
||||
if (resolvedTheme === 'dark') {
|
||||
body.classList.add('swagger-dark-theme');
|
||||
} else {
|
||||
body.classList.remove('swagger-dark-theme');
|
||||
}
|
||||
|
||||
return () => {
|
||||
body.classList.remove('swagger-dark-theme');
|
||||
};
|
||||
}, [resolvedTheme]);
|
||||
|
||||
return <SwaggerUI spec={OpenAPIV1} displayOperationId={true} />;
|
||||
};
|
||||
|
||||
|
||||
@ -24,6 +24,13 @@ export const sendCompletedEmail = async ({ documentId, requestMetadata }: SendDo
|
||||
include: {
|
||||
documentData: true,
|
||||
Recipient: true,
|
||||
User: true,
|
||||
team: {
|
||||
select: {
|
||||
id: true,
|
||||
url: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
@ -35,27 +42,35 @@ export const sendCompletedEmail = async ({ documentId, requestMetadata }: SendDo
|
||||
throw new Error('Document has no recipients');
|
||||
}
|
||||
|
||||
const buffer = await getFile(document.documentData);
|
||||
const { User: owner } = document;
|
||||
|
||||
await Promise.all(
|
||||
document.Recipient.map(async (recipient) => {
|
||||
const { email, name, token } = recipient;
|
||||
const completedDocument = await getFile(document.documentData);
|
||||
|
||||
const assetBaseUrl = NEXT_PUBLIC_WEBAPP_URL() || 'http://localhost:3000';
|
||||
|
||||
let documentOwnerDownloadLink = `${NEXT_PUBLIC_WEBAPP_URL()}/documents/${document.id}`;
|
||||
|
||||
if (document.team?.url) {
|
||||
documentOwnerDownloadLink = `${NEXT_PUBLIC_WEBAPP_URL()}/t/${document.team.url}/documents/${
|
||||
document.id
|
||||
}`;
|
||||
}
|
||||
|
||||
// If the document owner is not a recipient then send the email to them separately
|
||||
if (!document.Recipient.find((recipient) => recipient.email === owner.email)) {
|
||||
const template = createElement(DocumentCompletedEmailTemplate, {
|
||||
documentName: document.title,
|
||||
assetBaseUrl,
|
||||
downloadLink: `${NEXT_PUBLIC_WEBAPP_URL()}/sign/${token}/complete`,
|
||||
downloadLink: documentOwnerDownloadLink,
|
||||
});
|
||||
|
||||
await prisma.$transaction(
|
||||
async (tx) => {
|
||||
await mailer.sendMail({
|
||||
to: {
|
||||
address: email,
|
||||
name,
|
||||
to: [
|
||||
{
|
||||
name: owner.name || '',
|
||||
address: owner.email,
|
||||
},
|
||||
],
|
||||
from: {
|
||||
name: process.env.NEXT_PRIVATE_SMTP_FROM_NAME || 'Documenso',
|
||||
address: process.env.NEXT_PRIVATE_SMTP_FROM_ADDRESS || 'noreply@documenso.com',
|
||||
@ -66,12 +81,62 @@ export const sendCompletedEmail = async ({ documentId, requestMetadata }: SendDo
|
||||
attachments: [
|
||||
{
|
||||
filename: document.title,
|
||||
content: Buffer.from(buffer),
|
||||
content: Buffer.from(completedDocument),
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
await tx.documentAuditLog.create({
|
||||
await prisma.documentAuditLog.create({
|
||||
data: createDocumentAuditLogData({
|
||||
type: DOCUMENT_AUDIT_LOG_TYPE.EMAIL_SENT,
|
||||
documentId: document.id,
|
||||
user: null,
|
||||
requestMetadata,
|
||||
data: {
|
||||
emailType: 'DOCUMENT_COMPLETED',
|
||||
recipientEmail: owner.email,
|
||||
recipientName: owner.name,
|
||||
recipientId: owner.id,
|
||||
recipientRole: 'OWNER',
|
||||
isResending: false,
|
||||
},
|
||||
}),
|
||||
});
|
||||
}
|
||||
|
||||
await Promise.all(
|
||||
document.Recipient.map(async (recipient) => {
|
||||
const downloadLink = `${NEXT_PUBLIC_WEBAPP_URL()}/sign/${recipient.token}/complete`;
|
||||
|
||||
const template = createElement(DocumentCompletedEmailTemplate, {
|
||||
documentName: document.title,
|
||||
assetBaseUrl,
|
||||
downloadLink: recipient.email === owner.email ? documentOwnerDownloadLink : downloadLink,
|
||||
});
|
||||
|
||||
await mailer.sendMail({
|
||||
to: [
|
||||
{
|
||||
name: recipient.name,
|
||||
address: recipient.email,
|
||||
},
|
||||
],
|
||||
from: {
|
||||
name: process.env.NEXT_PRIVATE_SMTP_FROM_NAME || 'Documenso',
|
||||
address: process.env.NEXT_PRIVATE_SMTP_FROM_ADDRESS || 'noreply@documenso.com',
|
||||
},
|
||||
subject: 'Signing Complete!',
|
||||
html: render(template),
|
||||
text: render(template, { plainText: true }),
|
||||
attachments: [
|
||||
{
|
||||
filename: document.title,
|
||||
content: Buffer.from(completedDocument),
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
await prisma.documentAuditLog.create({
|
||||
data: createDocumentAuditLogData({
|
||||
type: DOCUMENT_AUDIT_LOG_TYPE.EMAIL_SENT,
|
||||
documentId: document.id,
|
||||
@ -87,9 +152,6 @@ export const sendCompletedEmail = async ({ documentId, requestMetadata }: SendDo
|
||||
},
|
||||
}),
|
||||
});
|
||||
},
|
||||
{ timeout: 30_000 },
|
||||
);
|
||||
}),
|
||||
);
|
||||
};
|
||||
|
||||
@ -65,7 +65,7 @@ export function DataTablePagination<TData>({
|
||||
</div>
|
||||
<div className="flex flex-wrap items-center gap-x-6 gap-y-4 lg:gap-x-8">
|
||||
<div className="flex items-center text-sm font-medium md:justify-center">
|
||||
Page {table.getState().pagination.pageIndex + 1} of {table.getPageCount()}
|
||||
Page {table.getState().pagination.pageIndex + 1} of {table.getPageCount() || 1}
|
||||
</div>
|
||||
|
||||
<div className="flex items-center gap-x-2">
|
||||
|
||||
@ -129,3 +129,12 @@
|
||||
.custom-scrollbar::-webkit-scrollbar-thumb:hover {
|
||||
background: rgb(100 116 139 / 0.5);
|
||||
}
|
||||
|
||||
/* Custom Swagger Dark Theme */
|
||||
.swagger-dark-theme .swagger-ui {
|
||||
filter: invert(88%) hue-rotate(180deg);
|
||||
}
|
||||
|
||||
.swagger-dark-theme .swagger-ui .microlight {
|
||||
filter: invert(100%) hue-rotate(180deg);
|
||||
}
|
||||
Reference in New Issue
Block a user