mirror of
https://github.com/documenso/documenso.git
synced 2025-11-13 08:13:56 +10:00
feat: add prisma json types (#1583)
This commit is contained in:
@ -1053,12 +1053,12 @@ export const ApiContractV1Implementation = createNextRoute(ApiContractV1, {
|
||||
.with('TEXT', () => ZTextFieldMeta.safeParse(fieldMeta))
|
||||
.with('SIGNATURE', 'INITIALS', 'DATE', 'EMAIL', 'NAME', () => ({
|
||||
success: true,
|
||||
data: {},
|
||||
data: undefined,
|
||||
}))
|
||||
.with('FREE_SIGNATURE', () => ({
|
||||
success: false,
|
||||
error: 'FREE_SIGNATURE is not supported',
|
||||
data: {},
|
||||
data: undefined,
|
||||
}))
|
||||
.exhaustive();
|
||||
|
||||
|
||||
@ -27,7 +27,7 @@ function createTempPdfFile() {
|
||||
'%PDF-1.4\n1 0 obj<</Type/Catalog/Pages 2 0 R>>endobj 2 0 obj<</Type/Pages/Kids[3 0 R]/Count 1>>endobj 3 0 obj<</Type/Page/MediaBox[0 0 612 792]/Parent 2 0 R>>endobj\nxref\n0 4\n0000000000 65535 f\n0000000009 00000 n\n0000000052 00000 n\n0000000101 00000 n\ntrailer<</Size 4/Root 1 0 R>>\nstartxref\n178\n%%EOF',
|
||||
);
|
||||
|
||||
fs.writeFileSync(tempFilePath, pdfContent);
|
||||
fs.writeFileSync(tempFilePath, new Uint8Array(pdfContent));
|
||||
return tempFilePath;
|
||||
}
|
||||
|
||||
|
||||
@ -13,7 +13,7 @@
|
||||
"author": "",
|
||||
"devDependencies": {
|
||||
"@playwright/test": "^1.18.1",
|
||||
"@types/node": "^20.8.2",
|
||||
"@types/node": "^20",
|
||||
"@documenso/lib": "*",
|
||||
"@documenso/prisma": "*",
|
||||
"@documenso/web": "*",
|
||||
|
||||
@ -15,6 +15,6 @@
|
||||
"eslint-plugin-package-json": "^0.10.4",
|
||||
"eslint-plugin-react": "^7.34.0",
|
||||
"eslint-plugin-unused-imports": "^3.1.0",
|
||||
"typescript": "5.2.2"
|
||||
"typescript": "5.6.2"
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -33,7 +33,7 @@ export const setupTwoFactorAuthentication = async ({
|
||||
|
||||
const accountName = user.email;
|
||||
const uri = createTOTPKeyURI(ISSUER, accountName, secret);
|
||||
const encodedSecret = base32.encode(secret);
|
||||
const encodedSecret = base32.encode(new Uint8Array(secret));
|
||||
|
||||
await prisma.user.update({
|
||||
where: {
|
||||
|
||||
@ -65,11 +65,6 @@ export const updateField = async ({
|
||||
},
|
||||
});
|
||||
|
||||
const newFieldMeta = {
|
||||
...(oldField.fieldMeta as FieldMeta),
|
||||
...fieldMeta,
|
||||
};
|
||||
|
||||
const field = prisma.$transaction(async (tx) => {
|
||||
const updatedField = await tx.field.update({
|
||||
where: {
|
||||
@ -83,7 +78,7 @@ export const updateField = async ({
|
||||
positionY: pageY,
|
||||
width: pageWidth,
|
||||
height: pageHeight,
|
||||
fieldMeta: newFieldMeta,
|
||||
fieldMeta,
|
||||
},
|
||||
include: {
|
||||
recipient: true,
|
||||
|
||||
8
packages/lib/types/document-form-values.ts
Normal file
8
packages/lib/types/document-form-values.ts
Normal file
@ -0,0 +1,8 @@
|
||||
import { z } from 'zod';
|
||||
|
||||
export const ZDocumentFormValuesSchema = z.record(
|
||||
z.string(),
|
||||
z.union([z.string(), z.boolean(), z.number()]),
|
||||
);
|
||||
|
||||
export type TDocumentFormValues = z.infer<typeof ZDocumentFormValuesSchema>;
|
||||
@ -9,36 +9,36 @@ export const ZBaseFieldMeta = z.object({
|
||||
|
||||
export type TBaseFieldMeta = z.infer<typeof ZBaseFieldMeta>;
|
||||
|
||||
export const ZInitialsFieldMeta = z.object({
|
||||
type: z.literal('initials').default('initials'),
|
||||
export const ZInitialsFieldMeta = ZBaseFieldMeta.extend({
|
||||
type: z.literal('initials'),
|
||||
fontSize: z.number().min(8).max(96).optional(),
|
||||
});
|
||||
|
||||
export type TInitialsFieldMeta = z.infer<typeof ZInitialsFieldMeta>;
|
||||
|
||||
export const ZNameFieldMeta = z.object({
|
||||
type: z.literal('name').default('name'),
|
||||
export const ZNameFieldMeta = ZBaseFieldMeta.extend({
|
||||
type: z.literal('name'),
|
||||
fontSize: z.number().min(8).max(96).optional(),
|
||||
});
|
||||
|
||||
export type TNameFieldMeta = z.infer<typeof ZNameFieldMeta>;
|
||||
|
||||
export const ZEmailFieldMeta = z.object({
|
||||
type: z.literal('email').default('email'),
|
||||
export const ZEmailFieldMeta = ZBaseFieldMeta.extend({
|
||||
type: z.literal('email'),
|
||||
fontSize: z.number().min(8).max(96).optional(),
|
||||
});
|
||||
|
||||
export type TEmailFieldMeta = z.infer<typeof ZEmailFieldMeta>;
|
||||
|
||||
export const ZDateFieldMeta = z.object({
|
||||
type: z.literal('date').default('date'),
|
||||
export const ZDateFieldMeta = ZBaseFieldMeta.extend({
|
||||
type: z.literal('date'),
|
||||
fontSize: z.number().min(8).max(96).optional(),
|
||||
});
|
||||
|
||||
export type TDateFieldMeta = z.infer<typeof ZDateFieldMeta>;
|
||||
|
||||
export const ZTextFieldMeta = ZBaseFieldMeta.extend({
|
||||
type: z.literal('text').default('text'),
|
||||
type: z.literal('text'),
|
||||
text: z.string().optional(),
|
||||
characterLimit: z.number().optional(),
|
||||
fontSize: z.number().min(8).max(96).optional(),
|
||||
@ -47,7 +47,7 @@ export const ZTextFieldMeta = ZBaseFieldMeta.extend({
|
||||
export type TTextFieldMeta = z.infer<typeof ZTextFieldMeta>;
|
||||
|
||||
export const ZNumberFieldMeta = ZBaseFieldMeta.extend({
|
||||
type: z.literal('number').default('number'),
|
||||
type: z.literal('number'),
|
||||
numberFormat: z.string().optional(),
|
||||
value: z.string().optional(),
|
||||
minValue: z.number().optional(),
|
||||
@ -58,7 +58,7 @@ export const ZNumberFieldMeta = ZBaseFieldMeta.extend({
|
||||
export type TNumberFieldMeta = z.infer<typeof ZNumberFieldMeta>;
|
||||
|
||||
export const ZRadioFieldMeta = ZBaseFieldMeta.extend({
|
||||
type: z.literal('radio').default('radio'),
|
||||
type: z.literal('radio'),
|
||||
values: z
|
||||
.array(
|
||||
z.object({
|
||||
@ -73,7 +73,7 @@ export const ZRadioFieldMeta = ZBaseFieldMeta.extend({
|
||||
export type TRadioFieldMeta = z.infer<typeof ZRadioFieldMeta>;
|
||||
|
||||
export const ZCheckboxFieldMeta = ZBaseFieldMeta.extend({
|
||||
type: z.literal('checkbox').default('checkbox'),
|
||||
type: z.literal('checkbox'),
|
||||
values: z
|
||||
.array(
|
||||
z.object({
|
||||
@ -90,30 +90,27 @@ export const ZCheckboxFieldMeta = ZBaseFieldMeta.extend({
|
||||
export type TCheckboxFieldMeta = z.infer<typeof ZCheckboxFieldMeta>;
|
||||
|
||||
export const ZDropdownFieldMeta = ZBaseFieldMeta.extend({
|
||||
type: z.literal('dropdown').default('dropdown'),
|
||||
type: z.literal('dropdown'),
|
||||
values: z.array(z.object({ value: z.string() })).optional(),
|
||||
defaultValue: z.string().optional(),
|
||||
});
|
||||
|
||||
export type TDropdownFieldMeta = z.infer<typeof ZDropdownFieldMeta>;
|
||||
|
||||
/**
|
||||
* This will parse empty objects to { "type": "initials" }
|
||||
*
|
||||
* Todo: Fix.
|
||||
*/
|
||||
export const ZFieldMetaSchema = z
|
||||
.union([
|
||||
ZBaseFieldMeta.extend(ZInitialsFieldMeta.shape),
|
||||
ZBaseFieldMeta.extend(ZNameFieldMeta.shape),
|
||||
ZBaseFieldMeta.extend(ZEmailFieldMeta.shape),
|
||||
ZBaseFieldMeta.extend(ZDateFieldMeta.shape),
|
||||
ZTextFieldMeta,
|
||||
ZNumberFieldMeta,
|
||||
ZRadioFieldMeta,
|
||||
ZCheckboxFieldMeta,
|
||||
ZDropdownFieldMeta,
|
||||
])
|
||||
.optional();
|
||||
export const ZFieldMetaNotOptionalSchema = z.discriminatedUnion('type', [
|
||||
ZInitialsFieldMeta,
|
||||
ZNameFieldMeta,
|
||||
ZEmailFieldMeta,
|
||||
ZDateFieldMeta,
|
||||
ZTextFieldMeta,
|
||||
ZNumberFieldMeta,
|
||||
ZRadioFieldMeta,
|
||||
ZCheckboxFieldMeta,
|
||||
ZDropdownFieldMeta,
|
||||
]);
|
||||
|
||||
export type TFieldMetaNotOptionalSchema = z.infer<typeof ZFieldMetaNotOptionalSchema>;
|
||||
|
||||
export const ZFieldMetaSchema = ZFieldMetaNotOptionalSchema.optional();
|
||||
|
||||
export type TFieldMetaSchema = z.infer<typeof ZFieldMetaSchema>;
|
||||
|
||||
@ -1,3 +1,4 @@
|
||||
/// <reference types="@documenso/prisma/types/types.d.ts" />
|
||||
import { PrismaClient } from '@prisma/client';
|
||||
import { Kysely, PostgresAdapter, PostgresIntrospector, PostgresQueryCompiler } from 'kysely';
|
||||
import kyselyExtension from 'prisma-extension-kysely';
|
||||
|
||||
@ -30,9 +30,10 @@
|
||||
"devDependencies": {
|
||||
"dotenv": "^16.3.1",
|
||||
"dotenv-cli": "^7.3.0",
|
||||
"prisma-json-types-generator": "^3.2.2",
|
||||
"prisma-kysely": "^1.8.0",
|
||||
"tsx": "^4.11.0",
|
||||
"typescript": "5.2.2",
|
||||
"typescript": "5.6.2",
|
||||
"zod-prisma-types": "3.1.9"
|
||||
}
|
||||
}
|
||||
@ -6,6 +6,10 @@ generator client {
|
||||
provider = "prisma-client-js"
|
||||
}
|
||||
|
||||
generator json {
|
||||
provider = "prisma-json-types-generator"
|
||||
}
|
||||
|
||||
generator zod {
|
||||
provider = "zod-prisma-types"
|
||||
createInputTypes = false
|
||||
@ -297,14 +301,14 @@ enum DocumentVisibility {
|
||||
ADMIN
|
||||
}
|
||||
|
||||
/// @zod.import(["import { ZDocumentAuthOptionsSchema } from '@documenso/lib/types/document-auth';"])
|
||||
/// @zod.import(["import { ZDocumentAuthOptionsSchema } from '@documenso/lib/types/document-auth';", "import { ZDocumentFormValuesSchema } from '@documenso/lib/types/document-form-values';"])
|
||||
model Document {
|
||||
id Int @id @default(autoincrement())
|
||||
externalId String? /// @zod.string.describe("A custom external ID you can use to identify the document.")
|
||||
userId Int /// @zod.number.describe("The ID of the user that created this document.")
|
||||
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
|
||||
authOptions Json? /// Todo: zod.custom.use(ZDocumentAuthOptionsSchema.describe("Hello"))
|
||||
formValues Json?
|
||||
authOptions Json? /// [DocumentAuthOptions] @zod.custom.use(ZDocumentAuthOptionsSchema)
|
||||
formValues Json? /// [DocumentFormValues] @zod.custom.use(ZDocumentFormValuesSchema)
|
||||
visibility DocumentVisibility @default(EVERYONE)
|
||||
title String
|
||||
status DocumentStatus @default(DRAFT)
|
||||
@ -373,6 +377,7 @@ enum DocumentDistributionMethod {
|
||||
NONE
|
||||
}
|
||||
|
||||
/// @zod.import(["import { ZDocumentEmailSettingsSchema } from '@documenso/lib/types/document-email';"])
|
||||
model DocumentMeta {
|
||||
id String @id @default(cuid())
|
||||
subject String?
|
||||
@ -387,7 +392,7 @@ model DocumentMeta {
|
||||
typedSignatureEnabled Boolean @default(true)
|
||||
language String @default("en")
|
||||
distributionMethod DocumentDistributionMethod @default(EMAIL)
|
||||
emailSettings Json?
|
||||
emailSettings Json? /// [DocumentEmailSettings] @zod.custom.use(ZDocumentEmailSettingsSchema)
|
||||
}
|
||||
|
||||
enum ReadStatus {
|
||||
@ -424,7 +429,7 @@ model Recipient {
|
||||
documentDeletedAt DateTime?
|
||||
expired DateTime?
|
||||
signedAt DateTime?
|
||||
authOptions Json? /// Todo: zod.custom.use(ZRecipientAuthOptionsSchema)
|
||||
authOptions Json? /// [RecipientAuthOptions] @zod.custom.use(ZRecipientAuthOptionsSchema)
|
||||
signingOrder Int? /// @zod.number.describe("The order in which the recipient should sign the document. Only works if the document is set to sequential signing.")
|
||||
rejectionReason String?
|
||||
role RecipientRole @default(SIGNER)
|
||||
@ -457,6 +462,7 @@ enum FieldType {
|
||||
DROPDOWN
|
||||
}
|
||||
|
||||
/// @zod.import(["import { ZFieldMetaNotOptionalSchema } from '@documenso/lib/types/field-meta';"])
|
||||
model Field {
|
||||
id Int @id @default(autoincrement())
|
||||
secondaryId String @unique @default(cuid())
|
||||
@ -475,7 +481,7 @@ model Field {
|
||||
template Template? @relation(fields: [templateId], references: [id], onDelete: Cascade)
|
||||
recipient Recipient @relation(fields: [recipientId], references: [id], onDelete: Cascade)
|
||||
signature Signature?
|
||||
fieldMeta Json? // Todo: Fix ZFieldMetaSchema before using it here.
|
||||
fieldMeta Json? /// [FieldMeta] @zod.custom.use(ZFieldMetaNotOptionalSchema)
|
||||
|
||||
@@index([documentId])
|
||||
@@index([templateId])
|
||||
@ -640,6 +646,7 @@ enum TemplateType {
|
||||
PRIVATE
|
||||
}
|
||||
|
||||
/// @zod.import(["import { ZDocumentEmailSettingsSchema } from '@documenso/lib/types/document-email';"])
|
||||
model TemplateMeta {
|
||||
id String @id @default(cuid())
|
||||
subject String?
|
||||
@ -655,9 +662,10 @@ model TemplateMeta {
|
||||
template Template @relation(fields: [templateId], references: [id], onDelete: Cascade)
|
||||
redirectUrl String?
|
||||
language String @default("en")
|
||||
emailSettings Json?
|
||||
emailSettings Json? /// [DocumentEmailSettings] @zod.custom.use(ZDocumentEmailSettingsSchema)
|
||||
}
|
||||
|
||||
/// @zod.import(["import { ZDocumentAuthOptionsSchema } from '@documenso/lib/types/document-auth';"])
|
||||
model Template {
|
||||
id Int @id @default(autoincrement())
|
||||
externalId String?
|
||||
@ -666,7 +674,7 @@ model Template {
|
||||
userId Int
|
||||
teamId Int?
|
||||
visibility DocumentVisibility @default(EVERYONE)
|
||||
authOptions Json?
|
||||
authOptions Json? /// [DocumentAuthOptions] @zod.custom.use(ZDocumentAuthOptionsSchema)
|
||||
templateMeta TemplateMeta?
|
||||
templateDocumentDataId String
|
||||
createdAt DateTime @default(now())
|
||||
|
||||
25
packages/prisma/types/types.d.ts
vendored
Normal file
25
packages/prisma/types/types.d.ts
vendored
Normal file
@ -0,0 +1,25 @@
|
||||
/* eslint-disable @typescript-eslint/no-namespace */
|
||||
import type {
|
||||
TDocumentAuthOptions,
|
||||
TRecipientAuthOptions,
|
||||
} from '@documenso/lib/types/document-auth';
|
||||
import type { TDocumentEmailSettings } from '@documenso/lib/types/document-email';
|
||||
import type { TDocumentFormValues } from '@documenso/lib/types/document-form-values';
|
||||
import type { TFieldMetaNotOptionalSchema } from '@documenso/lib/types/field-meta';
|
||||
|
||||
/**
|
||||
* Global types for Prisma.Json instances.
|
||||
*/
|
||||
declare global {
|
||||
namespace PrismaJson {
|
||||
type DocumentFormValues = TDocumentFormValues;
|
||||
type DocumentAuthOptions = TDocumentAuthOptions;
|
||||
type DocumentEmailSettings = TDocumentEmailSettings;
|
||||
|
||||
type RecipientAuthOptions = TRecipientAuthOptions;
|
||||
|
||||
type FieldMeta = TFieldMetaNotOptionalSchema;
|
||||
}
|
||||
}
|
||||
|
||||
export {};
|
||||
@ -26,9 +26,9 @@ export const updateSigningPlaceholder = ({ pdf }: UpdateSigningPlaceholderOption
|
||||
const newByteRange = `[${byteRange.join(' ')}]`.padEnd(byteRangeSlice.length, ' ');
|
||||
|
||||
const updatedPdf = Buffer.concat([
|
||||
pdf.subarray(0, byteRangeStart),
|
||||
Buffer.from(newByteRange),
|
||||
pdf.subarray(byteRangeEnd + 1),
|
||||
new Uint8Array(pdf.subarray(0, byteRangeStart)),
|
||||
new Uint8Array(Buffer.from(newByteRange)),
|
||||
new Uint8Array(pdf.subarray(byteRangeEnd + 1)),
|
||||
]);
|
||||
|
||||
if (updatedPdf.length !== length) {
|
||||
|
||||
@ -23,13 +23,14 @@ export const signWithGoogleCloudHSM = async ({ pdf }: SignWithGoogleCloudHSMOpti
|
||||
process.env.NEXT_PRIVATE_SIGNING_GCLOUD_APPLICATION_CREDENTIALS_CONTENTS
|
||||
) {
|
||||
if (!fs.existsSync(process.env.GOOGLE_APPLICATION_CREDENTIALS)) {
|
||||
fs.writeFileSync(
|
||||
process.env.GOOGLE_APPLICATION_CREDENTIALS,
|
||||
const contents = new Uint8Array(
|
||||
Buffer.from(
|
||||
process.env.NEXT_PRIVATE_SIGNING_GCLOUD_APPLICATION_CREDENTIALS_CONTENTS,
|
||||
'base64',
|
||||
),
|
||||
);
|
||||
|
||||
fs.writeFileSync(process.env.GOOGLE_APPLICATION_CREDENTIALS, contents);
|
||||
}
|
||||
}
|
||||
|
||||
@ -38,8 +39,8 @@ export const signWithGoogleCloudHSM = async ({ pdf }: SignWithGoogleCloudHSMOpti
|
||||
});
|
||||
|
||||
const pdfWithoutSignature = Buffer.concat([
|
||||
pdfWithPlaceholder.subarray(0, byteRange[1]),
|
||||
pdfWithPlaceholder.subarray(byteRange[2]),
|
||||
new Uint8Array(pdfWithPlaceholder.subarray(0, byteRange[1])),
|
||||
new Uint8Array(pdfWithPlaceholder.subarray(byteRange[2])),
|
||||
]);
|
||||
|
||||
const signatureLength = byteRange[2] - byteRange[1];
|
||||
@ -70,9 +71,9 @@ export const signWithGoogleCloudHSM = async ({ pdf }: SignWithGoogleCloudHSMOpti
|
||||
const signatureAsHex = signature.toString('hex');
|
||||
|
||||
const signedPdf = Buffer.concat([
|
||||
pdfWithPlaceholder.subarray(0, byteRange[1]),
|
||||
Buffer.from(`<${signatureAsHex.padEnd(signatureLength - 2, '0')}>`),
|
||||
pdfWithPlaceholder.subarray(byteRange[2]),
|
||||
new Uint8Array(pdfWithPlaceholder.subarray(0, byteRange[1])),
|
||||
new Uint8Array(Buffer.from(`<${signatureAsHex.padEnd(signatureLength - 2, '0')}>`)),
|
||||
new Uint8Array(pdfWithPlaceholder.subarray(byteRange[2])),
|
||||
]);
|
||||
|
||||
return signedPdf;
|
||||
|
||||
@ -15,8 +15,8 @@ export const signWithLocalCert = async ({ pdf }: SignWithLocalCertOptions) => {
|
||||
});
|
||||
|
||||
const pdfWithoutSignature = Buffer.concat([
|
||||
pdfWithPlaceholder.subarray(0, byteRange[1]),
|
||||
pdfWithPlaceholder.subarray(byteRange[2]),
|
||||
new Uint8Array(pdfWithPlaceholder.subarray(0, byteRange[1])),
|
||||
new Uint8Array(pdfWithPlaceholder.subarray(byteRange[2])),
|
||||
]);
|
||||
|
||||
const signatureLength = byteRange[2] - byteRange[1];
|
||||
@ -51,9 +51,9 @@ export const signWithLocalCert = async ({ pdf }: SignWithLocalCertOptions) => {
|
||||
const signatureAsHex = signature.toString('hex');
|
||||
|
||||
const signedPdf = Buffer.concat([
|
||||
pdfWithPlaceholder.subarray(0, byteRange[1]),
|
||||
Buffer.from(`<${signatureAsHex.padEnd(signatureLength - 2, '0')}>`),
|
||||
pdfWithPlaceholder.subarray(byteRange[2]),
|
||||
new Uint8Array(pdfWithPlaceholder.subarray(0, byteRange[1])),
|
||||
new Uint8Array(Buffer.from(`<${signatureAsHex.padEnd(signatureLength - 2, '0')}>`)),
|
||||
new Uint8Array(pdfWithPlaceholder.subarray(byteRange[2])),
|
||||
]);
|
||||
|
||||
return signedPdf;
|
||||
|
||||
@ -23,7 +23,7 @@
|
||||
"@types/react": "^18",
|
||||
"@types/react-dom": "^18",
|
||||
"react": "^18",
|
||||
"typescript": "5.2.2"
|
||||
"typescript": "5.6.2"
|
||||
},
|
||||
"dependencies": {
|
||||
"@documenso/lib": "*",
|
||||
|
||||
Reference in New Issue
Block a user