mirror of
https://github.com/documenso/documenso.git
synced 2025-11-18 18:51:37 +10:00
feat: separate document data from document
This commit is contained in:
@ -12,6 +12,8 @@
|
||||
],
|
||||
"scripts": {},
|
||||
"dependencies": {
|
||||
"@aws-sdk/s3-request-presigner": "^3.405.0",
|
||||
"@aws-sdk/client-s3": "^3.405.0",
|
||||
"@documenso/email": "*",
|
||||
"@documenso/prisma": "*",
|
||||
"@next-auth/prisma-adapter": "1.0.7",
|
||||
|
||||
10
packages/lib/server-only/document/create-document.ts
Normal file
10
packages/lib/server-only/document/create-document.ts
Normal file
@ -0,0 +1,10 @@
|
||||
'use server';
|
||||
|
||||
export type CreateDocumentOptions = {
|
||||
userId: number;
|
||||
fileName: string;
|
||||
};
|
||||
|
||||
export const createDocument = () => {
|
||||
//
|
||||
};
|
||||
@ -11,5 +11,8 @@ export const getDocumentById = async ({ id, userId }: GetDocumentByIdOptions) =>
|
||||
id,
|
||||
userId,
|
||||
},
|
||||
include: {
|
||||
documentData: true,
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
@ -17,6 +17,7 @@ export const getDocumentAndSenderByToken = async ({
|
||||
},
|
||||
include: {
|
||||
User: true,
|
||||
documentData: true,
|
||||
},
|
||||
});
|
||||
|
||||
|
||||
@ -18,8 +18,15 @@ export const sealDocument = async ({ documentId }: SealDocumentOptions) => {
|
||||
where: {
|
||||
id: documentId,
|
||||
},
|
||||
include: {
|
||||
documentData: true,
|
||||
},
|
||||
});
|
||||
|
||||
if (!document.documentData) {
|
||||
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`);
|
||||
}
|
||||
@ -48,7 +55,7 @@ export const sealDocument = async ({ documentId }: SealDocumentOptions) => {
|
||||
}
|
||||
|
||||
// !: Need to write the fields onto the document as a hard copy
|
||||
const { document: pdfData } = document;
|
||||
const { data: pdfData } = document.documentData;
|
||||
|
||||
const doc = await PDFDocument.load(pdfData);
|
||||
|
||||
@ -64,7 +71,11 @@ export const sealDocument = async ({ documentId }: SealDocumentOptions) => {
|
||||
status: DocumentStatus.COMPLETED,
|
||||
},
|
||||
data: {
|
||||
document: Buffer.from(pdfBytes).toString('base64'),
|
||||
documentData: {
|
||||
update: {
|
||||
data: Buffer.from(pdfBytes).toString('base64'),
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
@ -0,0 +1,19 @@
|
||||
-- CreateEnum
|
||||
CREATE TYPE "DocumentDataType" AS ENUM ('S3_PATH', 'BYTES', 'BYTES_64');
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE "DocumentData" (
|
||||
"id" TEXT NOT NULL,
|
||||
"type" "DocumentDataType" NOT NULL,
|
||||
"data" TEXT NOT NULL,
|
||||
"initialData" TEXT NOT NULL,
|
||||
"documentId" INTEGER NOT NULL,
|
||||
|
||||
CONSTRAINT "DocumentData_pkey" PRIMARY KEY ("id")
|
||||
);
|
||||
|
||||
-- CreateIndex
|
||||
CREATE UNIQUE INDEX "DocumentData_documentId_key" ON "DocumentData"("documentId");
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "DocumentData" ADD CONSTRAINT "DocumentData_documentId_fkey" FOREIGN KEY ("documentId") REFERENCES "Document"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
||||
@ -0,0 +1,14 @@
|
||||
INSERT INTO
|
||||
"DocumentData" ("id", "type", "data", "initialData", "documentId") (
|
||||
SELECT
|
||||
CAST(gen_random_uuid() AS TEXT),
|
||||
'BYTES_64',
|
||||
d."document",
|
||||
d."document",
|
||||
d."id"
|
||||
FROM
|
||||
"Document" d
|
||||
WHERE
|
||||
d."id" IS NOT NULL
|
||||
AND d."document" IS NOT NULL
|
||||
);
|
||||
@ -0,0 +1,3 @@
|
||||
-- AlterTable
|
||||
ALTER TABLE "Document" ADD COLUMN "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
ADD COLUMN "updatedAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP;
|
||||
@ -0,0 +1,8 @@
|
||||
/*
|
||||
Warnings:
|
||||
|
||||
- You are about to drop the column `document` on the `Document` table. All the data in the column will be lost.
|
||||
|
||||
*/
|
||||
-- AlterTable
|
||||
ALTER TABLE "Document" DROP COLUMN "document";
|
||||
@ -85,15 +85,35 @@ enum DocumentStatus {
|
||||
}
|
||||
|
||||
model Document {
|
||||
id Int @id @default(autoincrement())
|
||||
created DateTime @default(now())
|
||||
userId Int
|
||||
User User @relation(fields: [userId], references: [id], onDelete: Cascade)
|
||||
title String
|
||||
status DocumentStatus @default(DRAFT)
|
||||
document String
|
||||
Recipient Recipient[]
|
||||
Field Field[]
|
||||
id Int @id @default(autoincrement())
|
||||
created DateTime @default(now())
|
||||
userId Int
|
||||
User User @relation(fields: [userId], references: [id], onDelete: Cascade)
|
||||
title String
|
||||
status DocumentStatus @default(DRAFT)
|
||||
Recipient Recipient[]
|
||||
Field Field[]
|
||||
documentData DocumentData?
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @updatedAt @default(now())
|
||||
}
|
||||
|
||||
enum DocumentDataType {
|
||||
S3_PATH
|
||||
BYTES
|
||||
BYTES_64
|
||||
}
|
||||
|
||||
model DocumentData {
|
||||
id String @id @default(cuid())
|
||||
type DocumentDataType
|
||||
data String
|
||||
initialData String
|
||||
documentId Int
|
||||
|
||||
Document Document @relation(fields: [documentId], references: [id], onDelete: Cascade)
|
||||
|
||||
@@unique([documentId])
|
||||
}
|
||||
|
||||
enum ReadStatus {
|
||||
|
||||
5
packages/prisma/types/document-with-data.ts
Normal file
5
packages/prisma/types/document-with-data.ts
Normal file
@ -0,0 +1,5 @@
|
||||
import { Document, DocumentData } from '@documenso/prisma/client';
|
||||
|
||||
export type DocumentWithData = Document & {
|
||||
documentData?: DocumentData | null;
|
||||
};
|
||||
@ -1,17 +1,63 @@
|
||||
import { TRPCError } from '@trpc/server';
|
||||
|
||||
import { getDocumentById } from '@documenso/lib/server-only/document/get-document-by-id';
|
||||
import { getDocumentAndSenderByToken } from '@documenso/lib/server-only/document/get-document-by-token';
|
||||
import { sendDocument } from '@documenso/lib/server-only/document/send-document';
|
||||
import { setFieldsForDocument } from '@documenso/lib/server-only/field/set-fields-for-document';
|
||||
import { setRecipientsForDocument } from '@documenso/lib/server-only/recipient/set-recipients-for-document';
|
||||
|
||||
import { authenticatedProcedure, router } from '../trpc';
|
||||
import { authenticatedProcedure, procedure, router } from '../trpc';
|
||||
import {
|
||||
ZGetDocumentByIdQuerySchema,
|
||||
ZGetDocumentByTokenQuerySchema,
|
||||
ZSendDocumentMutationSchema,
|
||||
ZSetFieldsForDocumentMutationSchema,
|
||||
ZSetRecipientsForDocumentMutationSchema,
|
||||
} from './schema';
|
||||
|
||||
export const documentRouter = router({
|
||||
getDocumentById: authenticatedProcedure
|
||||
.input(ZGetDocumentByIdQuerySchema)
|
||||
.query(async ({ input, ctx }) => {
|
||||
try {
|
||||
const { id } = input;
|
||||
|
||||
console.log({
|
||||
id,
|
||||
userId: ctx.user.id,
|
||||
});
|
||||
|
||||
return await getDocumentById({
|
||||
id,
|
||||
userId: ctx.user.id,
|
||||
});
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
|
||||
throw new TRPCError({
|
||||
code: 'BAD_REQUEST',
|
||||
message: 'We were unable to find this document. Please try again later.',
|
||||
});
|
||||
}
|
||||
}),
|
||||
|
||||
getDocumentByToken: procedure.input(ZGetDocumentByTokenQuerySchema).query(async ({ input }) => {
|
||||
try {
|
||||
const { token } = input;
|
||||
|
||||
return await getDocumentAndSenderByToken({
|
||||
token,
|
||||
});
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
|
||||
throw new TRPCError({
|
||||
code: 'BAD_REQUEST',
|
||||
message: 'We were unable to find this document. Please try again later.',
|
||||
});
|
||||
}
|
||||
}),
|
||||
|
||||
setRecipientsForDocument: authenticatedProcedure
|
||||
.input(ZSetRecipientsForDocumentMutationSchema)
|
||||
.mutation(async ({ input, ctx }) => {
|
||||
|
||||
@ -2,6 +2,18 @@ import { z } from 'zod';
|
||||
|
||||
import { FieldType } from '@documenso/prisma/client';
|
||||
|
||||
export const ZGetDocumentByIdQuerySchema = z.object({
|
||||
id: z.number().min(1),
|
||||
});
|
||||
|
||||
export type TGetDocumentByIdQuerySchema = z.infer<typeof ZGetDocumentByIdQuerySchema>;
|
||||
|
||||
export const ZGetDocumentByTokenQuerySchema = z.object({
|
||||
token: z.string().min(1),
|
||||
});
|
||||
|
||||
export type TGetDocumentByTokenQuerySchema = z.infer<typeof ZGetDocumentByTokenQuerySchema>;
|
||||
|
||||
export const ZSetRecipientsForDocumentMutationSchema = z.object({
|
||||
documentId: z.number(),
|
||||
recipients: z.array(
|
||||
|
||||
7
packages/tsconfig/process-env.d.ts
vendored
7
packages/tsconfig/process-env.d.ts
vendored
@ -13,6 +13,13 @@ declare namespace NodeJS {
|
||||
NEXT_PRIVATE_STRIPE_API_KEY: string;
|
||||
NEXT_PRIVATE_STRIPE_WEBHOOK_SECRET: string;
|
||||
|
||||
NEXT_PRIVATE_UPLOAD_TRANSPORT?: 'database' | 's3';
|
||||
NEXT_PRIVATE_UPLOAD_ENDPOINT?: string;
|
||||
NEXT_PRIVATE_UPLOAD_REGION?: string;
|
||||
NEXT_PRIVATE_UPLOAD_BUCKET?: string;
|
||||
NEXT_PRIVATE_UPLOAD_ACCESS_KEY_ID?: string;
|
||||
NEXT_PRIVATE_UPLOAD_SECRET_ACCESS_KEY?: string;
|
||||
|
||||
NEXT_PRIVATE_SMTP_TRANSPORT?: 'mailchannels' | 'smtp-auth' | 'smtp-api';
|
||||
|
||||
NEXT_PRIVATE_MAILCHANNELS_API_KEY?: string;
|
||||
|
||||
Reference in New Issue
Block a user