mirror of
https://github.com/documenso/documenso.git
synced 2025-11-17 02:01:33 +10:00
Merge branch 'feat/refresh' into date-format-setting
This commit is contained in:
55
packages/lib/server-only/admin/get-all-documents.ts
Normal file
55
packages/lib/server-only/admin/get-all-documents.ts
Normal file
@ -0,0 +1,55 @@
|
||||
import { prisma } from '@documenso/prisma';
|
||||
import { Prisma } from '@documenso/prisma/client';
|
||||
|
||||
export interface FindDocumentsOptions {
|
||||
term?: string;
|
||||
page?: number;
|
||||
perPage?: number;
|
||||
}
|
||||
|
||||
export const findDocuments = async ({ term, page = 1, perPage = 10 }: FindDocumentsOptions) => {
|
||||
const termFilters: Prisma.DocumentWhereInput | undefined = !term
|
||||
? undefined
|
||||
: {
|
||||
title: {
|
||||
contains: term,
|
||||
mode: 'insensitive',
|
||||
},
|
||||
};
|
||||
|
||||
const [data, count] = await Promise.all([
|
||||
prisma.document.findMany({
|
||||
where: {
|
||||
...termFilters,
|
||||
},
|
||||
skip: Math.max(page - 1, 0) * perPage,
|
||||
take: perPage,
|
||||
orderBy: {
|
||||
createdAt: 'desc',
|
||||
},
|
||||
include: {
|
||||
User: {
|
||||
select: {
|
||||
id: true,
|
||||
name: true,
|
||||
email: true,
|
||||
},
|
||||
},
|
||||
Recipient: true,
|
||||
},
|
||||
}),
|
||||
prisma.document.count({
|
||||
where: {
|
||||
...termFilters,
|
||||
},
|
||||
}),
|
||||
]);
|
||||
|
||||
return {
|
||||
data,
|
||||
count,
|
||||
currentPage: Math.max(page, 1),
|
||||
perPage,
|
||||
totalPages: Math.ceil(count / perPage),
|
||||
};
|
||||
};
|
||||
13
packages/lib/server-only/admin/get-all-subscriptions.ts
Normal file
13
packages/lib/server-only/admin/get-all-subscriptions.ts
Normal file
@ -0,0 +1,13 @@
|
||||
import { prisma } from '@documenso/prisma';
|
||||
|
||||
export const findSubscriptions = async () => {
|
||||
return prisma.subscription.findMany({
|
||||
select: {
|
||||
id: true,
|
||||
status: true,
|
||||
createdAt: true,
|
||||
periodEnd: true,
|
||||
userId: true,
|
||||
},
|
||||
});
|
||||
};
|
||||
@ -9,9 +9,7 @@ export const getUsersWithSubscriptionsCount = async () => {
|
||||
return await prisma.user.count({
|
||||
where: {
|
||||
Subscription: {
|
||||
some: {
|
||||
status: SubscriptionStatus.ACTIVE,
|
||||
},
|
||||
status: SubscriptionStatus.ACTIVE,
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
28
packages/lib/server-only/admin/update-user.ts
Normal file
28
packages/lib/server-only/admin/update-user.ts
Normal file
@ -0,0 +1,28 @@
|
||||
import { prisma } from '@documenso/prisma';
|
||||
import { Role } from '@documenso/prisma/client';
|
||||
|
||||
export type UpdateUserOptions = {
|
||||
id: number;
|
||||
name: string | null | undefined;
|
||||
email: string | undefined;
|
||||
roles: Role[] | undefined;
|
||||
};
|
||||
|
||||
export const updateUser = async ({ id, name, email, roles }: UpdateUserOptions) => {
|
||||
await prisma.user.findFirstOrThrow({
|
||||
where: {
|
||||
id,
|
||||
},
|
||||
});
|
||||
|
||||
return await prisma.user.update({
|
||||
where: {
|
||||
id,
|
||||
},
|
||||
data: {
|
||||
name,
|
||||
email,
|
||||
roles,
|
||||
},
|
||||
});
|
||||
};
|
||||
13
packages/lib/server-only/document/delete-draft-document.ts
Normal file
13
packages/lib/server-only/document/delete-draft-document.ts
Normal file
@ -0,0 +1,13 @@
|
||||
'use server';
|
||||
|
||||
import { prisma } from '@documenso/prisma';
|
||||
import { DocumentStatus } from '@documenso/prisma/client';
|
||||
|
||||
export type DeleteDraftDocumentOptions = {
|
||||
id: number;
|
||||
userId: number;
|
||||
};
|
||||
|
||||
export const deleteDraftDocument = async ({ id, userId }: DeleteDraftDocumentOptions) => {
|
||||
return await prisma.document.delete({ where: { id, userId, status: DocumentStatus.DRAFT } });
|
||||
};
|
||||
@ -14,9 +14,10 @@ import { sendCompletedEmail } from './send-completed-email';
|
||||
|
||||
export type SealDocumentOptions = {
|
||||
documentId: number;
|
||||
sendEmail?: boolean;
|
||||
};
|
||||
|
||||
export const sealDocument = async ({ documentId }: SealDocumentOptions) => {
|
||||
export const sealDocument = async ({ documentId, sendEmail = true }: SealDocumentOptions) => {
|
||||
'use server';
|
||||
|
||||
const document = await prisma.document.findFirstOrThrow({
|
||||
@ -91,5 +92,7 @@ export const sealDocument = async ({ documentId }: SealDocumentOptions) => {
|
||||
},
|
||||
});
|
||||
|
||||
await sendCompletedEmail({ documentId });
|
||||
if (sendEmail) {
|
||||
await sendCompletedEmail({ documentId });
|
||||
}
|
||||
};
|
||||
|
||||
@ -5,6 +5,8 @@ import { render } from '@documenso/email/render';
|
||||
import { DocumentCompletedEmailTemplate } from '@documenso/email/templates/document-completed';
|
||||
import { prisma } from '@documenso/prisma';
|
||||
|
||||
import { getFile } from '../../universal/upload/get-file';
|
||||
|
||||
export interface SendDocumentOptions {
|
||||
documentId: number;
|
||||
}
|
||||
@ -15,6 +17,7 @@ export const sendCompletedEmail = async ({ documentId }: SendDocumentOptions) =>
|
||||
id: documentId,
|
||||
},
|
||||
include: {
|
||||
documentData: true,
|
||||
Recipient: true,
|
||||
},
|
||||
});
|
||||
@ -27,6 +30,8 @@ export const sendCompletedEmail = async ({ documentId }: SendDocumentOptions) =>
|
||||
throw new Error('Document has no recipients');
|
||||
}
|
||||
|
||||
const buffer = await getFile(document.documentData);
|
||||
|
||||
await Promise.all([
|
||||
document.Recipient.map(async (recipient) => {
|
||||
const { email, name, token } = recipient;
|
||||
@ -51,6 +56,12 @@ export const sendCompletedEmail = async ({ documentId }: SendDocumentOptions) =>
|
||||
subject: 'Signing Complete!',
|
||||
html: render(template),
|
||||
text: render(template, { plainText: true }),
|
||||
attachments: [
|
||||
{
|
||||
filename: document.title,
|
||||
content: Buffer.from(buffer),
|
||||
},
|
||||
],
|
||||
});
|
||||
}),
|
||||
]);
|
||||
|
||||
9
packages/lib/server-only/http/to-next-request.ts
Normal file
9
packages/lib/server-only/http/to-next-request.ts
Normal file
@ -0,0 +1,9 @@
|
||||
import { NextRequest } from 'next/server';
|
||||
|
||||
export const toNextRequest = (req: Request) => {
|
||||
const headers = Object.fromEntries(req.headers.entries());
|
||||
|
||||
return new NextRequest(req, {
|
||||
headers: headers,
|
||||
});
|
||||
};
|
||||
28
packages/lib/server-only/http/with-swr.ts
Normal file
28
packages/lib/server-only/http/with-swr.ts
Normal file
@ -0,0 +1,28 @@
|
||||
import { NextApiResponse } from 'next';
|
||||
import { NextResponse } from 'next/server';
|
||||
|
||||
type NarrowedResponse<T> = T extends NextResponse
|
||||
? NextResponse
|
||||
: T extends NextApiResponse<infer U>
|
||||
? NextApiResponse<U>
|
||||
: never;
|
||||
|
||||
export const withStaleWhileRevalidate = <T>(
|
||||
res: NarrowedResponse<T>,
|
||||
cacheInSeconds = 60,
|
||||
staleCacheInSeconds = 300,
|
||||
) => {
|
||||
if ('headers' in res) {
|
||||
res.headers.set(
|
||||
'Cache-Control',
|
||||
`public, s-maxage=${cacheInSeconds}, stale-while-revalidate=${staleCacheInSeconds}`,
|
||||
);
|
||||
} else {
|
||||
res.setHeader(
|
||||
'Cache-Control',
|
||||
`public, s-maxage=${cacheInSeconds}, stale-while-revalidate=${staleCacheInSeconds}`,
|
||||
);
|
||||
}
|
||||
|
||||
return res;
|
||||
};
|
||||
@ -0,0 +1,15 @@
|
||||
import { prisma } from '@documenso/prisma';
|
||||
|
||||
export type GetRecipientSignaturesOptions = {
|
||||
recipientId: number;
|
||||
};
|
||||
|
||||
export const getRecipientSignatures = async ({ recipientId }: GetRecipientSignaturesOptions) => {
|
||||
return await prisma.signature.findMany({
|
||||
where: {
|
||||
Field: {
|
||||
recipientId,
|
||||
},
|
||||
},
|
||||
});
|
||||
};
|
||||
@ -1,3 +1,4 @@
|
||||
/// <reference types="./stripe.d.ts" />
|
||||
import Stripe from 'stripe';
|
||||
|
||||
export const stripe = new Stripe(process.env.NEXT_PRIVATE_STRIPE_API_KEY ?? '', {
|
||||
|
||||
7
packages/lib/server-only/stripe/stripe.d.ts
vendored
Normal file
7
packages/lib/server-only/stripe/stripe.d.ts
vendored
Normal file
@ -0,0 +1,7 @@
|
||||
declare module 'stripe' {
|
||||
namespace Stripe {
|
||||
interface Product {
|
||||
features?: Array<{ name: string }>;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -7,7 +7,7 @@ export type GetSubscriptionByUserIdOptions = {
|
||||
};
|
||||
|
||||
export const getSubscriptionByUserId = async ({ userId }: GetSubscriptionByUserIdOptions) => {
|
||||
return prisma.subscription.findFirst({
|
||||
return await prisma.subscription.findFirst({
|
||||
where: {
|
||||
userId,
|
||||
},
|
||||
|
||||
25
packages/lib/server-only/user/delete-user.ts
Normal file
25
packages/lib/server-only/user/delete-user.ts
Normal file
@ -0,0 +1,25 @@
|
||||
import { prisma } from '@documenso/prisma';
|
||||
|
||||
export type DeleteUserOptions = {
|
||||
email: string;
|
||||
};
|
||||
|
||||
export const deleteUser = async ({ email }: DeleteUserOptions) => {
|
||||
const user = await prisma.user.findFirst({
|
||||
where: {
|
||||
email: {
|
||||
contains: email,
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
if (!user) {
|
||||
throw new Error(`User with email ${email} not found`);
|
||||
}
|
||||
|
||||
return await prisma.user.delete({
|
||||
where: {
|
||||
id: user.id,
|
||||
},
|
||||
});
|
||||
};
|
||||
57
packages/lib/server-only/user/get-all-users.ts
Normal file
57
packages/lib/server-only/user/get-all-users.ts
Normal file
@ -0,0 +1,57 @@
|
||||
import { prisma } from '@documenso/prisma';
|
||||
import { Prisma } from '@documenso/prisma/client';
|
||||
|
||||
type GetAllUsersProps = {
|
||||
username: string;
|
||||
email: string;
|
||||
page: number;
|
||||
perPage: number;
|
||||
};
|
||||
|
||||
export const findUsers = async ({
|
||||
username = '',
|
||||
email = '',
|
||||
page = 1,
|
||||
perPage = 10,
|
||||
}: GetAllUsersProps) => {
|
||||
const whereClause = Prisma.validator<Prisma.UserWhereInput>()({
|
||||
OR: [
|
||||
{
|
||||
name: {
|
||||
contains: username,
|
||||
mode: 'insensitive',
|
||||
},
|
||||
},
|
||||
{
|
||||
email: {
|
||||
contains: email,
|
||||
mode: 'insensitive',
|
||||
},
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
const [users, count] = await Promise.all([
|
||||
await prisma.user.findMany({
|
||||
include: {
|
||||
Subscription: true,
|
||||
Document: {
|
||||
select: {
|
||||
id: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
where: whereClause,
|
||||
skip: Math.max(page - 1, 0) * perPage,
|
||||
take: perPage,
|
||||
}),
|
||||
await prisma.user.count({
|
||||
where: whereClause,
|
||||
}),
|
||||
]);
|
||||
|
||||
return {
|
||||
users,
|
||||
totalPages: Math.ceil(count / perPage),
|
||||
};
|
||||
};
|
||||
@ -7,9 +7,14 @@ import { SALT_ROUNDS } from '../../constants/auth';
|
||||
export type UpdatePasswordOptions = {
|
||||
userId: number;
|
||||
password: string;
|
||||
currentPassword: string;
|
||||
};
|
||||
|
||||
export const updatePassword = async ({ userId, password }: UpdatePasswordOptions) => {
|
||||
export const updatePassword = async ({
|
||||
userId,
|
||||
password,
|
||||
currentPassword,
|
||||
}: UpdatePasswordOptions) => {
|
||||
// Existence check
|
||||
const user = await prisma.user.findFirstOrThrow({
|
||||
where: {
|
||||
@ -17,23 +22,29 @@ export const updatePassword = async ({ userId, password }: UpdatePasswordOptions
|
||||
},
|
||||
});
|
||||
|
||||
const hashedPassword = await hash(password, SALT_ROUNDS);
|
||||
|
||||
if (user.password) {
|
||||
// Compare the new password with the old password
|
||||
const isSamePassword = await compare(password, user.password);
|
||||
|
||||
if (isSamePassword) {
|
||||
throw new Error('Your new password cannot be the same as your old password.');
|
||||
}
|
||||
if (!user.password) {
|
||||
throw new Error('User has no password');
|
||||
}
|
||||
|
||||
const isCurrentPasswordValid = await compare(currentPassword, user.password);
|
||||
if (!isCurrentPasswordValid) {
|
||||
throw new Error('Current password is incorrect.');
|
||||
}
|
||||
|
||||
// Compare the new password with the old password
|
||||
const isSamePassword = await compare(password, user.password);
|
||||
if (isSamePassword) {
|
||||
throw new Error('Your new password cannot be the same as your old password.');
|
||||
}
|
||||
|
||||
const hashedNewPassword = await hash(password, SALT_ROUNDS);
|
||||
|
||||
const updatedUser = await prisma.user.update({
|
||||
where: {
|
||||
id: userId,
|
||||
},
|
||||
data: {
|
||||
password: hashedPassword,
|
||||
password: hashedNewPassword,
|
||||
},
|
||||
});
|
||||
|
||||
|
||||
Reference in New Issue
Block a user