mirror of
https://github.com/documenso/documenso.git
synced 2025-11-13 16:23:06 +10:00
wip: refresh design
This commit is contained in:
10
packages/lib/server-only/auth/hash.ts
Normal file
10
packages/lib/server-only/auth/hash.ts
Normal 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);
|
||||
};
|
||||
66
packages/lib/server-only/document/find-documents.ts
Normal file
66
packages/lib/server-only/document/find-documents.ts
Normal 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),
|
||||
};
|
||||
};
|
||||
15
packages/lib/server-only/document/get-document-by-id.ts
Normal file
15
packages/lib/server-only/document/get-document-by-id.ts
Normal 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,
|
||||
},
|
||||
});
|
||||
};
|
||||
30
packages/lib/server-only/document/get-stats.ts
Normal file
30
packages/lib/server-only/document/get-stats.ts
Normal 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;
|
||||
};
|
||||
11
packages/lib/server-only/headers/get-locale.tsx
Normal file
11
packages/lib/server-only/headers/get-locale.tsx
Normal 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;
|
||||
};
|
||||
26
packages/lib/server-only/pdf/insert-image-in-pdf.ts
Normal file
26
packages/lib/server-only/pdf/insert-image-in-pdf.ts
Normal 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');
|
||||
}
|
||||
50
packages/lib/server-only/pdf/insert-text-in-pdf.ts
Normal file
50
packages/lib/server-only/pdf/insert-text-in-pdf.ts
Normal 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');
|
||||
}
|
||||
8
packages/lib/server-only/redis/index.ts
Normal file
8
packages/lib/server-only/redis/index.ts
Normal 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 ?? '',
|
||||
});
|
||||
9
packages/lib/server-only/stripe/index.ts
Normal file
9
packages/lib/server-only/stripe/index.ts
Normal 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 };
|
||||
35
packages/lib/server-only/user/create-user.ts
Normal file
35
packages/lib/server-only/user/create-user.ts
Normal 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,
|
||||
},
|
||||
});
|
||||
};
|
||||
13
packages/lib/server-only/user/get-user-by-email.ts
Normal file
13
packages/lib/server-only/user/get-user-by-email.ts
Normal 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(),
|
||||
},
|
||||
});
|
||||
};
|
||||
13
packages/lib/server-only/user/get-user-by-id.ts
Normal file
13
packages/lib/server-only/user/get-user-by-id.ts
Normal 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,
|
||||
},
|
||||
});
|
||||
};
|
||||
32
packages/lib/server-only/user/update-password.ts
Normal file
32
packages/lib/server-only/user/update-password.ts
Normal 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;
|
||||
};
|
||||
33
packages/lib/server-only/user/update-profile.ts
Normal file
33
packages/lib/server-only/user/update-profile.ts
Normal 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;
|
||||
};
|
||||
Reference in New Issue
Block a user