feat: separate document data from document

This commit is contained in:
Mythie
2023-09-07 19:27:21 +10:00
parent 0f38be4e41
commit 72bec7bc34
22 changed files with 300 additions and 44 deletions

View File

@ -10,6 +10,7 @@ import { redis } from '@documenso/lib/server-only/redis';
import { Stripe, stripe } from '@documenso/lib/server-only/stripe';
import { prisma } from '@documenso/prisma';
import {
DocumentDataType,
DocumentStatus,
FieldType,
ReadStatus,
@ -85,16 +86,33 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
const now = new Date();
const bytes64 = readFileSync('./public/documenso-supporter-pledge.pdf').toString('base64');
const document = await prisma.document.create({
data: {
title: 'Documenso Supporter Pledge.pdf',
status: DocumentStatus.COMPLETED,
userId: user.id,
document: readFileSync('./public/documenso-supporter-pledge.pdf').toString('base64'),
created: now,
documentData: {
create: {
type: DocumentDataType.BYTES_64,
data: bytes64,
initialData: bytes64,
},
},
},
include: {
documentData: true,
},
});
const { documentData } = document;
if (!documentData) {
throw new Error(`Document ${document.id} has no document data`);
}
const recipient = await prisma.recipient.create({
data: {
name: user.name ?? '',
@ -122,16 +140,16 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
});
if (signatureDataUrl) {
document.document = await insertImageInPDF(
document.document,
documentData.data = await insertImageInPDF(
documentData.data,
signatureDataUrl,
Number(field.positionX),
Number(field.positionY),
field.page,
);
} else {
document.document = await insertTextInPDF(
document.document,
documentData.data = await insertTextInPDF(
documentData.data,
signatureText ?? '',
Number(field.positionX),
Number(field.positionY),
@ -153,7 +171,11 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
id: document.id,
},
data: {
document: document.document,
documentData: {
update: {
data: documentData.data,
},
},
},
}),
]);

View File

@ -4,7 +4,8 @@ import { useState } from 'react';
import { useRouter } from 'next/navigation';
import { Document, Field, Recipient, User } from '@documenso/prisma/client';
import { Field, Recipient, User } from '@documenso/prisma/client';
import { DocumentWithData } from '@documenso/prisma/types/document-with-data';
import { cn } from '@documenso/ui/lib/utils';
import { Card, CardContent } from '@documenso/ui/primitives/card';
import { AddFieldsFormPartial } from '@documenso/ui/primitives/document-flow/add-fields';
@ -28,7 +29,7 @@ import { completeDocument } from '~/components/forms/edit-document/add-subject.a
export type EditDocumentFormProps = {
className?: string;
user: User;
document: Document;
document: DocumentWithData;
recipients: Recipient[];
fields: Field[];
};
@ -45,9 +46,11 @@ export const EditDocumentForm = ({
const { toast } = useToast();
const router = useRouter();
const { documentData } = document;
const [step, setStep] = useState<EditDocumentStep>('signers');
const documentUrl = `data:application/pdf;base64,${document.document}`;
const documentUrl = `data:application/pdf;base64,${documentData?.data}`;
const documentFlow: Record<EditDocumentStep, DocumentFlowStep> = {
signers: {

View File

@ -36,10 +36,12 @@ export default async function DocumentPage({ params }: DocumentPageProps) {
userId: session.id,
}).catch(() => null);
if (!document) {
if (!document || !document.documentData) {
redirect('/documents');
}
const { documentData } = document;
const [recipients, fields] = await Promise.all([
await getRecipientsForDocument({
documentId,
@ -91,7 +93,7 @@ export default async function DocumentPage({ params }: DocumentPageProps) {
{document.status === InternalDocumentStatus.COMPLETED && (
<div className="mx-auto mt-12 max-w-2xl">
<LazyPDFViewer document={`data:application/pdf;base64,${document.document}`} />
<LazyPDFViewer document={`data:application/pdf;base64,${documentData.data}`} />
</div>
)}
</div>

View File

@ -14,8 +14,17 @@ import {
XCircle,
} from 'lucide-react';
import { useSession } from 'next-auth/react';
import { match } from 'ts-pattern';
import { Document, DocumentStatus, Recipient, User } from '@documenso/prisma/client';
import {
Document,
DocumentDataType,
DocumentStatus,
Recipient,
User,
} from '@documenso/prisma/client';
import { DocumentWithData } from '@documenso/prisma/types/document-with-data';
import { trpc } from '@documenso/trpc/client';
import {
DropdownMenu,
DropdownMenuContent,
@ -47,17 +56,42 @@ export const DataTableActionDropdown = ({ row }: DataTableActionDropdownProps) =
const isComplete = row.status === DocumentStatus.COMPLETED;
// const isSigned = recipient?.signingStatus === SigningStatus.SIGNED;
const onDownloadClick = () => {
let decodedDocument = row.document;
const onDownloadClick = async () => {
let document: DocumentWithData | null = null;
try {
decodedDocument = atob(decodedDocument);
} catch (err) {
// We're just going to ignore this error and try to download the document
console.error(err);
if (!recipient) {
document = await trpc.document.getDocumentById.query({
id: row.id,
});
} else {
document = await trpc.document.getDocumentByToken.query({
token: recipient.token,
});
}
const documentBytes = Uint8Array.from(decodedDocument.split('').map((c) => c.charCodeAt(0)));
const documentData = document?.documentData;
if (!documentData) {
return;
}
const documentBytes = await match(documentData.type)
.with(DocumentDataType.BYTES, () =>
Uint8Array.from(documentData.data, (c) => c.charCodeAt(0)),
)
.with(DocumentDataType.BYTES_64, () =>
Uint8Array.from(
atob(documentData.data)
.split('')
.map((c) => c.charCodeAt(0)),
),
)
.with(DocumentDataType.S3_PATH, async () =>
fetch(documentData.data)
.then(async (res) => res.arrayBuffer())
.then((buffer) => new Uint8Array(buffer)),
)
.exhaustive();
const blob = new Blob([documentBytes], {
type: 'application/pdf',

View File

@ -30,10 +30,12 @@ export default async function CompletedSigningPage({
token,
}).catch(() => null);
if (!document) {
if (!document || !document.documentData) {
return notFound();
}
const { documentData } = document;
const [fields, recipient] = await Promise.all([
getFieldsForToken({ token }),
getRecipientByToken({ token }),
@ -91,7 +93,7 @@ export default async function CompletedSigningPage({
<DownloadButton
className="flex-1"
fileName={document.title}
document={document.status === DocumentStatus.COMPLETED ? document.document : undefined}
document={document.status === DocumentStatus.COMPLETED ? documentData.data : undefined}
disabled={document.status !== DocumentStatus.COMPLETED}
/>
</div>

View File

@ -40,13 +40,15 @@ export default async function SigningPage({ params: { token } }: SigningPageProp
viewedDocument({ token }),
]);
if (!document) {
if (!document || !document.documentData) {
return notFound();
}
const { documentData } = document;
const user = await getServerComponentSession();
const documentUrl = `data:application/pdf;base64,${document.document}`;
const documentUrl = `data:application/pdf;base64,${documentData.data}`;
return (
<SigningProvider email={recipient.email} fullName={recipient.name} signature={user?.signature}>

View File

@ -5,7 +5,7 @@ import { readFileSync } from 'fs';
import { getServerSession } from '@documenso/lib/next-auth/get-server-session';
import { prisma } from '@documenso/prisma';
import { DocumentStatus } from '@documenso/prisma/client';
import { DocumentDataType, DocumentStatus } from '@documenso/prisma/client';
import {
TCreateDocumentRequestSchema,
@ -55,12 +55,20 @@ export default async function handler(
const fileBuffer = readFileSync(file.filepath);
const bytes64 = fileBuffer.toString('base64');
const document = await prisma.document.create({
data: {
title: file.originalFilename ?? file.newFilename,
status: DocumentStatus.DRAFT,
userId: user.id,
document: fileBuffer.toString('base64'),
documentData: {
create: {
type: DocumentDataType.BYTES_64,
data: bytes64,
initialData: bytes64,
},
},
created: new Date(),
},
});

View File

@ -10,6 +10,7 @@ import { redis } from '@documenso/lib/server-only/redis';
import { Stripe, stripe } from '@documenso/lib/server-only/stripe';
import { prisma } from '@documenso/prisma';
import {
DocumentDataType,
DocumentStatus,
FieldType,
ReadStatus,
@ -85,16 +86,33 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
const now = new Date();
const bytes64 = readFileSync('./public/documenso-supporter-pledge.pdf').toString('base64');
const document = await prisma.document.create({
data: {
title: 'Documenso Supporter Pledge.pdf',
status: DocumentStatus.COMPLETED,
userId: user.id,
document: readFileSync('./public/documenso-supporter-pledge.pdf').toString('base64'),
created: now,
documentData: {
create: {
type: DocumentDataType.BYTES_64,
data: bytes64,
initialData: bytes64,
},
},
},
include: {
documentData: true,
},
});
const { documentData } = document;
if (!documentData) {
throw new Error(`Document ${document.id} has no document data`);
}
const recipient = await prisma.recipient.create({
data: {
name: user.name ?? '',
@ -122,16 +140,16 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
});
if (signatureDataUrl) {
document.document = await insertImageInPDF(
document.document,
documentData.data = await insertImageInPDF(
documentData.data,
signatureDataUrl,
field.positionX.toNumber(),
field.positionY.toNumber(),
field.page,
);
} else {
document.document = await insertTextInPDF(
document.document,
documentData.data = await insertTextInPDF(
documentData.data,
signatureText ?? '',
field.positionX.toNumber(),
field.positionY.toNumber(),
@ -153,7 +171,11 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
id: document.id,
},
data: {
document: document.document,
documentData: {
update: {
data: documentData.data,
},
},
},
}),
]);