Merge branch 'main' into feat/webhook-implementation

This commit is contained in:
Lucas Smith
2024-02-26 12:47:21 +11:00
committed by GitHub
196 changed files with 10026 additions and 1102 deletions

View File

@ -1,9 +1,10 @@
import { TRPCError } from '@trpc/server';
import { updateUser } from '@documenso/lib/server-only/admin/update-user';
import { upsertSiteSetting } from '@documenso/lib/server-only/site-settings/upsert-site-setting';
import { adminProcedure, router } from '../trpc';
import { ZUpdateProfileMutationByAdminSchema } from './schema';
import { ZUpdateProfileMutationByAdminSchema, ZUpdateSiteSettingMutationSchema } from './schema';
export const adminRouter = router({
updateUser: adminProcedure
@ -20,4 +21,24 @@ export const adminRouter = router({
});
}
}),
updateSiteSetting: adminProcedure
.input(ZUpdateSiteSettingMutationSchema)
.mutation(async ({ ctx, input }) => {
try {
const { id, enabled, data } = input;
return await upsertSiteSetting({
id,
enabled,
data,
userId: ctx.user.id,
});
} catch (err) {
throw new TRPCError({
code: 'BAD_REQUEST',
message: 'We were unable to update the site setting provided.',
});
}
}),
});

View File

@ -1,6 +1,8 @@
import { Role } from '@prisma/client';
import z from 'zod';
import { ZSiteSettingSchema } from '@documenso/lib/server-only/site-settings/schema';
export const ZUpdateProfileMutationByAdminSchema = z.object({
id: z.number().min(1),
name: z.string().nullish(),
@ -11,3 +13,7 @@ export const ZUpdateProfileMutationByAdminSchema = z.object({
export type TUpdateProfileMutationByAdminSchema = z.infer<
typeof ZUpdateProfileMutationByAdminSchema
>;
export const ZUpdateSiteSettingMutationSchema = ZSiteSettingSchema;
export type TUpdateSiteSettingMutationSchema = z.infer<typeof ZUpdateSiteSettingMutationSchema>;

View File

@ -0,0 +1,83 @@
import { TRPCError } from '@trpc/server';
import { createApiToken } from '@documenso/lib/server-only/public-api/create-api-token';
import { deleteTokenById } from '@documenso/lib/server-only/public-api/delete-api-token-by-id';
import { getUserTokens } from '@documenso/lib/server-only/public-api/get-all-user-tokens';
import { getApiTokenById } from '@documenso/lib/server-only/public-api/get-api-token-by-id';
import { authenticatedProcedure, router } from '../trpc';
import {
ZCreateTokenMutationSchema,
ZDeleteTokenByIdMutationSchema,
ZGetApiTokenByIdQuerySchema,
} from './schema';
export const apiTokenRouter = router({
getTokens: authenticatedProcedure.query(async ({ ctx }) => {
try {
return await getUserTokens({ userId: ctx.user.id });
} catch (e) {
throw new TRPCError({
code: 'BAD_REQUEST',
message: 'We were unable to find your API tokens. Please try again.',
});
}
}),
getTokenById: authenticatedProcedure
.input(ZGetApiTokenByIdQuerySchema)
.query(async ({ input, ctx }) => {
try {
const { id } = input;
return await getApiTokenById({
id,
userId: ctx.user.id,
});
} catch (e) {
throw new TRPCError({
code: 'BAD_REQUEST',
message: 'We were unable to find this API token. Please try again.',
});
}
}),
createToken: authenticatedProcedure
.input(ZCreateTokenMutationSchema)
.mutation(async ({ input, ctx }) => {
try {
const { tokenName, teamId, expirationDate } = input;
return await createApiToken({
userId: ctx.user.id,
teamId,
tokenName,
expiresIn: expirationDate,
});
} catch (e) {
throw new TRPCError({
code: 'BAD_REQUEST',
message: 'We were unable to create an API token. Please try again.',
});
}
}),
deleteTokenById: authenticatedProcedure
.input(ZDeleteTokenByIdMutationSchema)
.mutation(async ({ input, ctx }) => {
try {
const { id, teamId } = input;
return await deleteTokenById({
id,
teamId,
userId: ctx.user.id,
});
} catch (e) {
throw new TRPCError({
code: 'BAD_REQUEST',
message: 'We were unable to delete this API Token. Please try again.',
});
}
}),
});

View File

@ -0,0 +1,22 @@
import { z } from 'zod';
export const ZGetApiTokenByIdQuerySchema = z.object({
id: z.number().min(1),
});
export type TGetApiTokenByIdQuerySchema = z.infer<typeof ZGetApiTokenByIdQuerySchema>;
export const ZCreateTokenMutationSchema = z.object({
teamId: z.number().optional(),
tokenName: z.string().min(3, { message: 'The token name should be 3 characters or longer' }),
expirationDate: z.string().nullable(),
});
export type TCreateTokenMutationSchema = z.infer<typeof ZCreateTokenMutationSchema>;
export const ZDeleteTokenByIdMutationSchema = z.object({
id: z.number().min(1),
teamId: z.number().optional(),
});
export type TDeleteTokenByIdMutationSchema = z.infer<typeof ZDeleteTokenByIdMutationSchema>;

View File

@ -6,29 +6,27 @@ import { upsertDocumentMeta } from '@documenso/lib/server-only/document-meta/ups
import { createDocument } from '@documenso/lib/server-only/document/create-document';
import { deleteDocument } from '@documenso/lib/server-only/document/delete-document';
import { duplicateDocumentById } from '@documenso/lib/server-only/document/duplicate-document-by-id';
import { findDocumentAuditLogs } from '@documenso/lib/server-only/document/find-document-audit-logs';
import { getDocumentById } from '@documenso/lib/server-only/document/get-document-by-id';
import { getDocumentAndSenderByToken } from '@documenso/lib/server-only/document/get-document-by-token';
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';
import { updateTitle } from '@documenso/lib/server-only/document/update-title';
import { setFieldsForDocument } from '@documenso/lib/server-only/field/set-fields-for-document';
import { setRecipientsForDocument } from '@documenso/lib/server-only/recipient/set-recipients-for-document';
import { symmetricEncrypt } from '@documenso/lib/universal/crypto';
import { extractNextApiRequestMetadata } from '@documenso/lib/universal/extract-request-metadata';
import { authenticatedProcedure, procedure, router } from '../trpc';
import {
ZCreateDocumentMutationSchema,
ZDeleteDraftDocumentMutationSchema,
ZDeleteDraftDocumentMutationSchema as ZDeleteDocumentMutationSchema,
ZFindDocumentAuditLogsQuerySchema,
ZGetDocumentByIdQuerySchema,
ZGetDocumentByTokenQuerySchema,
ZResendDocumentMutationSchema,
ZSearchDocumentsMutationSchema,
ZSendDocumentMutationSchema,
ZSetFieldsForDocumentMutationSchema,
ZSetPasswordForDocumentMutationSchema,
ZSetRecipientsForDocumentMutationSchema,
ZSetTitleForDocumentMutationSchema,
} from './schema';
@ -104,14 +102,19 @@ export const documentRouter = router({
}),
deleteDocument: authenticatedProcedure
.input(ZDeleteDraftDocumentMutationSchema)
.input(ZDeleteDocumentMutationSchema)
.mutation(async ({ input, ctx }) => {
try {
const { id, status } = input;
const { id, teamId } = input;
const userId = ctx.user.id;
return await deleteDocument({ id, userId, status });
return await deleteDocument({
id,
userId,
teamId,
requestMetadata: extractNextApiRequestMetadata(ctx.req),
});
} catch (err) {
console.error(err);
@ -122,66 +125,47 @@ export const documentRouter = router({
}
}),
findDocumentAuditLogs: authenticatedProcedure
.input(ZFindDocumentAuditLogsQuerySchema)
.query(async ({ input, ctx }) => {
try {
const { page, perPage, documentId, cursor, filterForRecentActivity, orderBy } = input;
return await findDocumentAuditLogs({
page,
perPage,
documentId,
cursor,
filterForRecentActivity,
orderBy,
userId: ctx.user.id,
});
} catch (err) {
console.error(err);
throw new TRPCError({
code: 'BAD_REQUEST',
message: 'We were unable to find audit logs for this document. Please try again later.',
});
}
}),
setTitleForDocument: authenticatedProcedure
.input(ZSetTitleForDocumentMutationSchema)
.mutation(async ({ input, ctx }) => {
const { documentId, title } = input;
const { documentId, teamId, title } = input;
const userId = ctx.user.id;
return await updateTitle({
title,
userId,
teamId,
documentId,
requestMetadata: extractNextApiRequestMetadata(ctx.req),
});
}),
setRecipientsForDocument: authenticatedProcedure
.input(ZSetRecipientsForDocumentMutationSchema)
.mutation(async ({ input, ctx }) => {
try {
const { documentId, recipients } = input;
return await setRecipientsForDocument({
userId: ctx.user.id,
documentId,
recipients,
requestMetadata: extractNextApiRequestMetadata(ctx.req),
});
} catch (err) {
console.error(err);
throw new TRPCError({
code: 'BAD_REQUEST',
message:
'We were unable to set the recipients for this document. Please try again later.',
});
}
}),
setFieldsForDocument: authenticatedProcedure
.input(ZSetFieldsForDocumentMutationSchema)
.mutation(async ({ input, ctx }) => {
try {
const { documentId, fields } = input;
return await setFieldsForDocument({
userId: ctx.user.id,
documentId,
fields,
requestMetadata: extractNextApiRequestMetadata(ctx.req),
});
} catch (err) {
console.error(err);
throw new TRPCError({
code: 'BAD_REQUEST',
message: 'We were unable to set the fields for this document. Please try again later.',
});
}
}),
setPasswordForDocument: authenticatedProcedure
.input(ZSetPasswordForDocumentMutationSchema)
.mutation(async ({ input, ctx }) => {
@ -219,7 +203,7 @@ export const documentRouter = router({
.input(ZSendDocumentMutationSchema)
.mutation(async ({ input, ctx }) => {
try {
const { documentId, meta } = input;
const { documentId, teamId, meta } = input;
if (meta.message || meta.subject || meta.timezone || meta.dateFormat || meta.redirectUrl) {
await upsertDocumentMeta({
@ -237,6 +221,7 @@ export const documentRouter = router({
return await sendDocument({
userId: ctx.user.id,
documentId,
teamId,
requestMetadata: extractNextApiRequestMetadata(ctx.req),
});
} catch (err) {

View File

@ -1,7 +1,20 @@
import { z } from 'zod';
import { URL_REGEX } from '@documenso/lib/constants/url-regex';
import { DocumentStatus, FieldType, RecipientRole } from '@documenso/prisma/client';
import { ZBaseTableSearchParamsSchema } from '@documenso/lib/types/search-params';
import { FieldType, RecipientRole } from '@documenso/prisma/client';
export const ZFindDocumentAuditLogsQuerySchema = ZBaseTableSearchParamsSchema.extend({
documentId: z.number().min(1),
cursor: z.string().optional(),
filterForRecentActivity: z.boolean().optional(),
orderBy: z
.object({
column: z.enum(['createdAt', 'type']),
direction: z.enum(['asc', 'desc']),
})
.optional(),
});
export const ZGetDocumentByIdQuerySchema = z.object({
id: z.number().min(1),
@ -26,6 +39,7 @@ export type TCreateDocumentMutationSchema = z.infer<typeof ZCreateDocumentMutati
export const ZSetTitleForDocumentMutationSchema = z.object({
documentId: z.number(),
teamId: z.number().min(1).optional(),
title: z.string().min(1),
});
@ -33,6 +47,7 @@ export type TSetTitleForDocumentMutationSchema = z.infer<typeof ZSetTitleForDocu
export const ZSetRecipientsForDocumentMutationSchema = z.object({
documentId: z.number(),
teamId: z.number().min(1).optional(),
recipients: z.array(
z.object({
id: z.number().nullish(),
@ -69,6 +84,7 @@ export type TSetFieldsForDocumentMutationSchema = z.infer<
export const ZSendDocumentMutationSchema = z.object({
documentId: z.number(),
teamId: z.number().optional(),
meta: z.object({
subject: z.string(),
message: z.string(),
@ -102,7 +118,7 @@ export type TSendDocumentMutationSchema = z.infer<typeof ZSendDocumentMutationSc
export const ZDeleteDraftDocumentMutationSchema = z.object({
id: z.number().min(1),
status: z.nativeEnum(DocumentStatus),
teamId: z.number().min(1).optional(),
});
export type TDeleteDraftDocumentMutationSchema = z.infer<typeof ZDeleteDraftDocumentMutationSchema>;

View File

@ -1,5 +1,6 @@
import { TRPCError } from '@trpc/server';
import { deleteUser } from '@documenso/lib/server-only/user/delete-user';
import { findUserSecurityAuditLogs } from '@documenso/lib/server-only/user/find-user-security-audit-logs';
import { forgotPassword } from '@documenso/lib/server-only/user/forgot-password';
import { getUserById } from '@documenso/lib/server-only/user/get-user-by-id';
@ -155,4 +156,23 @@ export const profileRouter = router({
});
}
}),
deleteAccount: authenticatedProcedure.mutation(async ({ ctx }) => {
try {
const user = ctx.user;
return await deleteUser(user);
} catch (err) {
let message = 'We were unable to delete your account. Please try again.';
if (err instanceof Error) {
message = err.message;
}
throw new TRPCError({
code: 'BAD_REQUEST',
message,
});
}
}),
});

View File

@ -17,11 +17,12 @@ export const recipientRouter = router({
.input(ZAddSignersMutationSchema)
.mutation(async ({ input, ctx }) => {
try {
const { documentId, signers } = input;
const { documentId, teamId, signers } = input;
return await setRecipientsForDocument({
userId: ctx.user.id,
documentId,
teamId,
recipients: signers.map((signer) => ({
id: signer.nativeId,
email: signer.email,
@ -53,6 +54,7 @@ export const recipientRouter = router({
id: signer.nativeId,
email: signer.email,
name: signer.name,
role: signer.role,
})),
});
} catch (err) {

View File

@ -5,6 +5,7 @@ import { RecipientRole } from '@documenso/prisma/client';
export const ZAddSignersMutationSchema = z
.object({
documentId: z.number(),
teamId: z.number().optional(),
signers: z.array(
z.object({
nativeId: z.number().optional(),
@ -34,6 +35,7 @@ export const ZAddTemplateSignersMutationSchema = z
nativeId: z.number().optional(),
email: z.string().email().min(1),
name: z.string(),
role: z.nativeEnum(RecipientRole),
}),
),
})

View File

@ -1,4 +1,5 @@
import { adminRouter } from './admin-router/router';
import { apiTokenRouter } from './api-token-router/router';
import { authRouter } from './auth-router/router';
import { cryptoRouter } from './crypto/router';
import { documentRouter } from './document-router/router';
@ -22,6 +23,7 @@ export const appRouter = router({
recipient: recipientRouter,
admin: adminRouter,
shareLink: shareLinkRouter,
apiToken: apiTokenRouter,
singleplayer: singleplayerRouter,
team: teamRouter,
template: templateRouter,

View File

@ -22,6 +22,7 @@ export const mapField = (
.with(FieldType.DATE, () => DateTime.now().toFormat('yyyy-MM-dd hh:mm a'))
.with(FieldType.EMAIL, () => signer.email)
.with(FieldType.NAME, () => signer.name)
.with(FieldType.TEXT, () => signer.customText)
.otherwise(() => '');
return {

View File

@ -12,6 +12,7 @@ export const ZCreateSinglePlayerDocumentMutationSchema = z.object({
email: z.string().email().min(1),
name: z.string(),
signature: z.string(),
customText: z.string(),
}),
fields: z.array(
z.object({

View File

@ -3,10 +3,11 @@ import { z } from 'zod';
import { PROTECTED_TEAM_URLS } from '@documenso/lib/constants/teams';
import { TeamMemberRole } from '@documenso/prisma/client';
// Consider refactoring to use ZBaseTableSearchParamsSchema.
const GenericFindQuerySchema = z.object({
term: z.string().optional(),
page: z.number().optional(),
perPage: z.number().optional(),
page: z.number().min(1).optional(),
perPage: z.number().min(1).optional(),
});
/**

View File

@ -41,7 +41,7 @@ export const templateRouter = router({
.input(ZCreateDocumentFromTemplateMutationSchema)
.mutation(async ({ input, ctx }) => {
try {
const { templateId } = input;
const { templateId, teamId } = input;
const limits = await getServerLimits({ email: ctx.user.email });
@ -51,7 +51,9 @@ export const templateRouter = router({
return await createDocumentFromTemplate({
templateId,
teamId,
userId: ctx.user.id,
recipients: input.recipients,
});
} catch (err) {
throw new TRPCError({

View File

@ -1,5 +1,7 @@
import { z } from 'zod';
import { RecipientRole } from '@documenso/prisma/client';
export const ZCreateTemplateMutationSchema = z.object({
title: z.string().min(1).trim(),
teamId: z.number().optional(),
@ -8,6 +10,16 @@ export const ZCreateTemplateMutationSchema = z.object({
export const ZCreateDocumentFromTemplateMutationSchema = z.object({
templateId: z.number(),
teamId: z.number().optional(),
recipients: z
.array(
z.object({
email: z.string().email(),
name: z.string(),
role: z.nativeEnum(RecipientRole),
}),
)
.optional(),
});
export const ZDuplicateTemplateMutationSchema = z.object({