wip: refresh design

This commit is contained in:
Mythie
2023-06-09 18:21:18 +10:00
parent 76b2fb5edd
commit 159bcade7b
432 changed files with 19640 additions and 29359 deletions

View File

@ -0,0 +1,10 @@
import { hashSync as bcryptHashSync } from 'bcrypt';
import { SALT_ROUNDS } from '../../constants/auth';
/**
* @deprecated Use the methods built into `bcrypt` instead
*/
export const hashSync = (password: string) => {
return bcryptHashSync(password, SALT_ROUNDS);
};

View File

@ -0,0 +1,66 @@
import { prisma } from '@documenso/prisma';
import { Document, DocumentStatus, Prisma } from '@documenso/prisma/client';
import { FindResultSet } from '../../types/find-result-set';
export interface FindDocumentsOptions {
userId: number;
term?: string;
status?: DocumentStatus;
page?: number;
perPage?: number;
orderBy?: {
column: keyof Omit<Document, 'document'>;
direction: 'asc' | 'desc';
};
}
export const findDocuments = async ({
userId,
term,
status,
page = 1,
perPage = 10,
orderBy,
}: FindDocumentsOptions): Promise<FindResultSet<Document>> => {
const orderByColumn = orderBy?.column ?? 'created';
const orderByDirection = orderBy?.direction ?? 'desc';
const filters: Prisma.DocumentWhereInput = {
status,
userId,
};
if (term) {
filters.title = {
contains: term,
mode: 'insensitive',
};
}
const [data, count] = await Promise.all([
prisma.document.findMany({
where: {
...filters,
},
skip: Math.max(page - 1, 0) * perPage,
take: perPage,
orderBy: {
[orderByColumn]: orderByDirection,
},
}),
prisma.document.count({
where: {
...filters,
},
}),
]);
return {
data,
count,
currentPage: Math.max(page, 1),
perPage,
totalPages: Math.ceil(count / perPage),
};
};

View File

@ -0,0 +1,15 @@
import { prisma } from '@documenso/prisma';
export interface GetDocumentByIdOptions {
id: number;
userId: number;
}
export const getDocumentById = async ({ id, userId }: GetDocumentByIdOptions) => {
return await prisma.document.findFirstOrThrow({
where: {
id,
userId,
},
});
};

View File

@ -0,0 +1,30 @@
import { prisma } from '@documenso/prisma';
import { DocumentStatus } from '@documenso/prisma/client';
export type GetStatsInput = {
userId: number;
};
export const getStats = async ({ userId }: GetStatsInput) => {
const result = await prisma.document.groupBy({
by: ['status'],
_count: {
_all: true,
},
where: {
userId,
},
});
const stats: Record<DocumentStatus, number> = {
[DocumentStatus.DRAFT]: 0,
[DocumentStatus.PENDING]: 0,
[DocumentStatus.COMPLETED]: 0,
};
result.forEach((stat) => {
stats[stat.status] = stat._count._all;
});
return stats;
};

View File

@ -0,0 +1,11 @@
import { headers } from 'next/headers';
export const getLocale = () => {
const headerItems = headers();
const locales = headerItems.get('accept-language') ?? 'en-US';
const [locale] = locales.split(',');
return locale;
};

View File

@ -0,0 +1,26 @@
import { PDFDocument } from 'pdf-lib';
export async function insertImageInPDF(
pdfAsBase64: string,
image: string | Uint8Array | ArrayBuffer,
positionX: number,
positionY: number,
page = 0,
): Promise<string> {
const existingPdfBytes = pdfAsBase64;
const pdfDoc = await PDFDocument.load(existingPdfBytes);
const pages = pdfDoc.getPages();
const pdfPage = pages[page];
const pngImage = await pdfDoc.embedPng(image);
const drawSize = { width: 192, height: 64 };
pdfPage.drawImage(pngImage, {
x: positionX,
y: pdfPage.getHeight() - positionY - drawSize.height,
width: drawSize.width,
height: drawSize.height,
});
const pdfAsUint8Array = await pdfDoc.save();
return Buffer.from(pdfAsUint8Array).toString('base64');
}

View File

@ -0,0 +1,50 @@
import fontkit from '@pdf-lib/fontkit';
import * as fs from 'fs';
import { PDFDocument, StandardFonts, rgb } from 'pdf-lib';
export async function insertTextInPDF(
pdfAsBase64: string,
text: string,
positionX: number,
positionY: number,
page = 0,
useHandwritingFont = true,
): Promise<string> {
const fontBytes = fs.readFileSync('./public/fonts/caveat.ttf');
const pdfDoc = await PDFDocument.load(pdfAsBase64);
pdfDoc.registerFontkit(fontkit);
const font = await pdfDoc.embedFont(useHandwritingFont ? fontBytes : StandardFonts.Helvetica);
const pages = pdfDoc.getPages();
const pdfPage = pages[page];
const textSize = useHandwritingFont ? 50 : 15;
const textWidth = font.widthOfTextAtSize(text, textSize);
const textHeight = font.heightAtSize(textSize);
const fieldSize = { width: 250, height: 64 };
// Because pdf-lib use a bottom-left coordinate system, we need to invert the y position
// we then center the text in the middle by adding half the height of the text
// plus the height of the field and divide the result by 2
const invertedYPosition =
pdfPage.getHeight() - positionY - (fieldSize.height + textHeight / 2) / 2;
// We center the text by adding the width of the field, subtracting the width of the text
// and dividing the result by 2
const centeredXPosition = positionX + (fieldSize.width - textWidth) / 2;
pdfPage.drawText(text, {
x: centeredXPosition,
y: invertedYPosition,
size: textSize,
color: rgb(0, 0, 0),
font,
});
const pdfAsUint8Array = await pdfDoc.save();
return Buffer.from(pdfAsUint8Array).toString('base64');
}

View File

@ -0,0 +1,8 @@
/* eslint-disable turbo/no-undeclared-env-vars */
import { Redis } from '@upstash/redis';
// !: We're null coalescing here because we don't want local builds to fail.
export const redis = new Redis({
url: process.env.NEXT_PRIVATE_REDIS_URL ?? '',
token: process.env.NEXT_PRIVATE_REDIS_TOKEN ?? '',
});

View File

@ -0,0 +1,9 @@
import Stripe from 'stripe';
// eslint-disable-next-line turbo/no-undeclared-env-vars
export const stripe = new Stripe(process.env.NEXT_PRIVATE_STRIPE_API_KEY!, {
apiVersion: '2022-11-15',
typescript: true,
});
export { Stripe };

View File

@ -0,0 +1,35 @@
import { hash } from 'bcrypt';
import { prisma } from '@documenso/prisma';
import { IdentityProvider } from '@documenso/prisma/client';
import { SALT_ROUNDS } from '../../constants/auth';
export interface CreateUserOptions {
name: string;
email: string;
password: string;
}
export const createUser = async ({ name, email, password }: CreateUserOptions) => {
const hashedPassword = await hash(password, SALT_ROUNDS);
const userExists = await prisma.user.findFirst({
where: {
email: email.toLowerCase(),
},
});
if (userExists) {
throw new Error('User already exists');
}
return await prisma.user.create({
data: {
name,
email: email.toLowerCase(),
password: hashedPassword,
identityProvider: IdentityProvider.DOCUMENSO,
},
});
};

View File

@ -0,0 +1,13 @@
import { prisma } from '@documenso/prisma';
export interface GetUserByEmailOptions {
email: string;
}
export const getUserByEmail = async ({ email }: GetUserByEmailOptions) => {
return await prisma.user.findFirstOrThrow({
where: {
email: email.toLowerCase(),
},
});
};

View File

@ -0,0 +1,13 @@
import { prisma } from '@documenso/prisma';
export interface GetUserByIdOptions {
id: number;
}
export const getUserById = async ({ id }: GetUserByIdOptions) => {
return await prisma.user.findFirstOrThrow({
where: {
id,
},
});
};

View File

@ -0,0 +1,32 @@
import { hash } from 'bcrypt';
import { prisma } from '@documenso/prisma';
import { SALT_ROUNDS } from '../../constants/auth';
export type UpdatePasswordOptions = {
userId: number;
password: string;
};
export const updatePassword = async ({ userId, password }: UpdatePasswordOptions) => {
// Existence check
await prisma.user.findFirstOrThrow({
where: {
id: userId,
},
});
const hashedPassword = await hash(password, SALT_ROUNDS);
const updatedUser = await prisma.user.update({
where: {
id: userId,
},
data: {
password: hashedPassword,
},
});
return updatedUser;
};

View File

@ -0,0 +1,33 @@
import { prisma } from '@documenso/prisma';
export type UpdateProfileOptions = {
userId: number;
name: string;
signature: string;
};
export const updateProfile = async ({
userId,
name,
// TODO: Actually use signature
signature: _signature,
}: UpdateProfileOptions) => {
// Existence check
await prisma.user.findFirstOrThrow({
where: {
id: userId,
},
});
const updatedUser = await prisma.user.update({
where: {
id: userId,
},
data: {
name,
// signature,
},
});
return updatedUser;
};