chore: merge main

This commit is contained in:
Catalin Documenso
2025-06-24 10:49:08 +03:00
787 changed files with 55308 additions and 42985 deletions

View File

@ -0,0 +1,105 @@
import type { Document, Prisma } from '@prisma/client';
import { DocumentStatus, RecipientRole } from '@prisma/client';
import type { FindResultResponse } from '@documenso/lib/types/search-params';
import { maskRecipientTokensForDocument } from '@documenso/lib/utils/mask-recipient-tokens-for-document';
import { prisma } from '@documenso/prisma';
import { authenticatedProcedure } from '../trpc';
import { ZFindInboxRequestSchema, ZFindInboxResponseSchema } from './find-inbox.types';
export const findInboxRoute = authenticatedProcedure
.input(ZFindInboxRequestSchema)
.output(ZFindInboxResponseSchema)
.query(async ({ input, ctx }) => {
const { page, perPage } = input;
const userId = ctx.user.id;
return await findInbox({
userId,
page,
perPage,
});
});
export type FindInboxOptions = {
userId: number;
page?: number;
perPage?: number;
orderBy?: {
column: keyof Omit<Document, 'document'>;
direction: 'asc' | 'desc';
};
};
export const findInbox = async ({ userId, page = 1, perPage = 10, orderBy }: FindInboxOptions) => {
const user = await prisma.user.findFirstOrThrow({
where: {
id: userId,
},
});
const orderByColumn = orderBy?.column ?? 'createdAt';
const orderByDirection = orderBy?.direction ?? 'desc';
const whereClause: Prisma.DocumentWhereInput = {
status: {
not: DocumentStatus.DRAFT,
},
deletedAt: null,
recipients: {
some: {
email: user.email,
role: {
not: RecipientRole.CC,
},
},
},
};
const [data, count] = await Promise.all([
prisma.document.findMany({
where: whereClause,
skip: Math.max(page - 1, 0) * perPage,
take: perPage,
orderBy: {
[orderByColumn]: orderByDirection,
},
include: {
user: {
select: {
id: true,
name: true,
email: true,
},
},
recipients: true,
team: {
select: {
id: true,
url: true,
},
},
},
}),
prisma.document.count({
where: whereClause,
}),
]);
const maskedData = data.map((document) =>
maskRecipientTokensForDocument({
document,
user,
}),
);
return {
data: maskedData,
count,
currentPage: Math.max(page, 1),
perPage,
totalPages: Math.ceil(count / perPage),
} satisfies FindResultResponse<typeof data>;
};

View File

@ -0,0 +1,13 @@
// import type { OpenApiMeta } from 'trpc-to-openapi';
import type { z } from 'zod';
import { ZDocumentManySchema } from '@documenso/lib/types/document';
import { ZFindResultResponse, ZFindSearchParamsSchema } from '@documenso/lib/types/search-params';
export const ZFindInboxRequestSchema = ZFindSearchParamsSchema;
export const ZFindInboxResponseSchema = ZFindResultResponse.extend({
data: ZDocumentManySchema.array(),
});
export type TFindInboxResponse = z.infer<typeof ZFindInboxResponseSchema>;

View File

@ -0,0 +1,35 @@
import { DocumentStatus, RecipientRole } from '@prisma/client';
import { prisma } from '@documenso/prisma';
import { authenticatedProcedure } from '../trpc';
import { ZGetInboxCountRequestSchema, ZGetInboxCountResponseSchema } from './get-inbox-count.types';
export const getInboxCountRoute = authenticatedProcedure
.input(ZGetInboxCountRequestSchema)
.output(ZGetInboxCountResponseSchema)
.query(async ({ input, ctx }) => {
const { readStatus } = input ?? {};
const userEmail = ctx.user.email;
const count = await prisma.recipient.count({
where: {
email: userEmail,
readStatus,
role: {
not: RecipientRole.CC,
},
document: {
status: {
not: DocumentStatus.DRAFT,
},
deletedAt: null,
},
},
});
return {
count,
};
});

View File

@ -0,0 +1,15 @@
// import type { OpenApiMeta } from 'trpc-to-openapi';
import { ReadStatus } from '@prisma/client';
import { z } from 'zod';
export const ZGetInboxCountRequestSchema = z
.object({
readStatus: z.nativeEnum(ReadStatus).optional(),
})
.optional();
export const ZGetInboxCountResponseSchema = z.object({
count: z.number(),
});
export type TGetInboxCountResponse = z.infer<typeof ZGetInboxCountResponseSchema>;

View File

@ -19,7 +19,6 @@ import { getDocumentAndSenderByToken } from '@documenso/lib/server-only/document
import { getDocumentWithDetailsById } from '@documenso/lib/server-only/document/get-document-with-details-by-id';
import type { GetStatsInput } from '@documenso/lib/server-only/document/get-stats';
import { getStats } from '@documenso/lib/server-only/document/get-stats';
import { moveDocumentToTeam } from '@documenso/lib/server-only/document/move-document-to-team';
import { resendDocument } from '@documenso/lib/server-only/document/resend-document';
import { searchDocumentsWithKeyword } from '@documenso/lib/server-only/document/search-documents-with-keyword';
import { sendDocument } from '@documenso/lib/server-only/document/send-document';
@ -28,6 +27,8 @@ import { getPresignPostUrl } from '@documenso/lib/universal/upload/server-action
import { isDocumentCompleted } from '@documenso/lib/utils/document';
import { authenticatedProcedure, procedure, router } from '../trpc';
import { findInboxRoute } from './find-inbox';
import { getInboxCountRoute } from './get-inbox-count';
import {
ZCreateDocumentRequestSchema,
ZCreateDocumentV2RequestSchema,
@ -49,8 +50,6 @@ import {
ZGetDocumentByTokenQuerySchema,
ZGetDocumentWithDetailsByIdRequestSchema,
ZGetDocumentWithDetailsByIdResponseSchema,
ZMoveDocumentToTeamResponseSchema,
ZMoveDocumentToTeamSchema,
ZResendDocumentMutationSchema,
ZSearchDocumentsMutationSchema,
ZSetSigningOrderForDocumentMutationSchema,
@ -59,6 +58,11 @@ import {
import { updateDocumentRoute } from './update-document';
export const documentRouter = router({
inbox: {
find: findInboxRoute,
getCount: getInboxCountRoute,
},
/**
* @private
*/
@ -174,7 +178,7 @@ export const documentRouter = router({
teamId: team.id,
teamEmail: team.teamEmail?.email,
senderIds,
currentTeamMemberRole: team.currentTeamMember?.role,
currentTeamMemberRole: team.currentTeamRole,
currentUserEmail: user.email,
userId: user.id,
};
@ -255,7 +259,7 @@ export const documentRouter = router({
.input(ZCreateDocumentV2RequestSchema)
.output(ZCreateDocumentV2ResponseSchema)
.mutation(async ({ input, ctx }) => {
const { teamId } = ctx;
const { teamId, user } = ctx;
const {
title,
@ -267,7 +271,7 @@ export const documentRouter = router({
meta,
} = input;
const { remaining } = await getServerLimits({ email: ctx.user.email, teamId });
const { remaining } = await getServerLimits({ userId: user.id, teamId });
if (remaining.documents <= 0) {
throw new AppError(AppErrorCode.LIMIT_EXCEEDED, {
@ -325,10 +329,10 @@ export const documentRouter = router({
// })
.input(ZCreateDocumentRequestSchema)
.mutation(async ({ input, ctx }) => {
const { teamId } = ctx;
const { user, teamId } = ctx;
const { title, documentDataId, timezone, folderId } = input;
const { remaining } = await getServerLimits({ email: ctx.user.email, teamId });
const { remaining } = await getServerLimits({ userId: user.id, teamId });
if (remaining.documents <= 0) {
throw new AppError(AppErrorCode.LIMIT_EXCEEDED, {
@ -338,7 +342,7 @@ export const documentRouter = router({
}
return await createDocument({
userId: ctx.user.id,
userId: user.id,
teamId,
title,
documentDataId,
@ -381,33 +385,6 @@ export const documentRouter = router({
return ZGenericSuccessResponse;
}),
/**
* @public
*/
moveDocumentToTeam: authenticatedProcedure
.meta({
openapi: {
method: 'POST',
path: '/document/move',
summary: 'Move document',
description: 'Move a document from your personal account to a team',
tags: ['Document'],
},
})
.input(ZMoveDocumentToTeamSchema)
.output(ZMoveDocumentToTeamResponseSchema)
.mutation(async ({ input, ctx }) => {
const { documentId, teamId } = input;
const userId = ctx.user.id;
return await moveDocumentToTeam({
documentId,
teamId,
userId,
requestMetadata: ctx.metadata,
});
}),
/**
* @private
*

View File

@ -206,8 +206,8 @@ export const ZCreateDocumentV2RequestSchema = z.object({
title: ZDocumentTitleSchema,
externalId: ZDocumentExternalIdSchema.optional(),
visibility: ZDocumentVisibilitySchema.optional(),
globalAccessAuth: ZDocumentAccessAuthTypesSchema.optional(),
globalActionAuth: ZDocumentActionAuthTypesSchema.optional(),
globalAccessAuth: z.array(ZDocumentAccessAuthTypesSchema).optional(),
globalActionAuth: z.array(ZDocumentActionAuthTypesSchema).optional(),
formValues: ZDocumentFormValuesSchema.optional(),
recipients: z
.array(
@ -344,10 +344,3 @@ export const ZDownloadAuditLogsMutationSchema = z.object({
export const ZDownloadCertificateMutationSchema = z.object({
documentId: z.number(),
});
export const ZMoveDocumentToTeamSchema = z.object({
documentId: z.number().describe('The ID of the document to move to a team.'),
teamId: z.number().describe('The ID of the team to move the document to.'),
});
export const ZMoveDocumentToTeamResponseSchema = ZDocumentLiteSchema;

View File

@ -43,8 +43,8 @@ export const ZUpdateDocumentRequestSchema = z.object({
title: ZDocumentTitleSchema.optional(),
externalId: ZDocumentExternalIdSchema.nullish(),
visibility: ZDocumentVisibilitySchema.optional(),
globalAccessAuth: ZDocumentAccessAuthTypesSchema.nullish(),
globalActionAuth: ZDocumentActionAuthTypesSchema.nullish(),
globalAccessAuth: z.array(ZDocumentAccessAuthTypesSchema).optional(),
globalActionAuth: z.array(ZDocumentActionAuthTypesSchema).optional(),
useLegacyFieldInsertion: z.boolean().optional(),
attachments: AttachmentSchema.pick({
id: true,