From 904948e2bcc80cb7da258a93f04464bf217dec98 Mon Sep 17 00:00:00 2001 From: David Nguyen Date: Fri, 6 Dec 2024 16:01:24 +0900 Subject: [PATCH] fix: refactor trpc errors (#1511) --- .../profile/delete-account-dialog.tsx | 27 +- .../sign/[token]/complete/claim-account.tsx | 37 +- .../web/src/components/forms/avatar-image.tsx | 29 +- apps/web/src/components/forms/password.tsx | 35 +- .../src/components/forms/reset-password.tsx | 35 +- apps/web/src/components/forms/signup.tsx | 28 +- apps/web/src/components/forms/v2/signup.tsx | 49 +- apps/web/src/pages/api/trpc/[trpc].ts | 3 +- packages/lib/server-only/crypto/encrypt.ts | 3 +- .../server-only/profile/set-avatar-image.ts | 9 +- packages/lib/server-only/user/create-user.ts | 15 +- .../lib/server-only/user/reset-password.ts | 9 +- .../lib/server-only/user/update-password.ts | 8 +- packages/trpc/server/admin-router/router.ts | 111 +--- .../trpc/server/api-token-router/router.ts | 76 +-- packages/trpc/server/auth-router/router.ts | 201 ++---- packages/trpc/server/crypto/router.ts | 8 - packages/trpc/server/crypto/schema.ts | 15 - .../trpc/server/document-router/router.ts | 590 ++++++------------ packages/trpc/server/field-router/router.ts | 149 ++--- packages/trpc/server/profile-router/router.ts | 262 ++------ .../trpc/server/recipient-router/router.ts | 126 ++-- packages/trpc/server/router.ts | 2 - .../trpc/server/share-link-router/router.ts | 33 +- .../trpc/server/singleplayer-router/router.ts | 260 ++++---- .../router.ts | 90 +-- packages/trpc/server/webhook-router/router.ts | 114 +--- 27 files changed, 806 insertions(+), 1518 deletions(-) delete mode 100644 packages/trpc/server/crypto/router.ts delete mode 100644 packages/trpc/server/crypto/schema.ts diff --git a/apps/web/src/app/(dashboard)/settings/profile/delete-account-dialog.tsx b/apps/web/src/app/(dashboard)/settings/profile/delete-account-dialog.tsx index 9b7499188..d0a13830c 100644 --- a/apps/web/src/app/(dashboard)/settings/profile/delete-account-dialog.tsx +++ b/apps/web/src/app/(dashboard)/settings/profile/delete-account-dialog.tsx @@ -7,7 +7,6 @@ import { useLingui } from '@lingui/react'; import { signOut } from 'next-auth/react'; import type { User } from '@documenso/prisma/client'; -import { TRPCClientError } from '@documenso/trpc/client'; import { trpc } from '@documenso/trpc/react'; import { Alert, AlertDescription, AlertTitle } from '@documenso/ui/primitives/alert'; import { Button } from '@documenso/ui/primitives/button'; @@ -52,30 +51,20 @@ export const DeleteAccountDialog = ({ className, user }: DeleteAccountDialogProp return await signOut({ callbackUrl: '/' }); } catch (err) { - if (err instanceof TRPCClientError && err.data?.code === 'BAD_REQUEST') { - toast({ - title: _(msg`An error occurred`), - description: err.message, - variant: 'destructive', - }); - } else { - toast({ - title: _(msg`An unknown error occurred`), - variant: 'destructive', - description: - err.message ?? - _( - msg`We encountered an unknown error while attempting to delete your account. Please try again later.`, - ), - }); - } + toast({ + title: _(msg`An unknown error occurred`), + variant: 'destructive', + description: _( + msg`We encountered an unknown error while attempting to delete your account. Please try again later.`, + ), + }); } }; return (
diff --git a/apps/web/src/app/(signing)/sign/[token]/complete/claim-account.tsx b/apps/web/src/app/(signing)/sign/[token]/complete/claim-account.tsx index cb36c4b55..9f2b85858 100644 --- a/apps/web/src/app/(signing)/sign/[token]/complete/claim-account.tsx +++ b/apps/web/src/app/(signing)/sign/[token]/complete/claim-account.tsx @@ -9,7 +9,7 @@ import { useForm } from 'react-hook-form'; import { z } from 'zod'; import { useAnalytics } from '@documenso/lib/client-only/hooks/use-analytics'; -import { TRPCClientError } from '@documenso/trpc/client'; +import { AppError } from '@documenso/lib/errors/app-error'; import { trpc } from '@documenso/trpc/react'; import { ZPasswordSchema } from '@documenso/trpc/server/auth-router/schema'; import { Button } from '@documenso/ui/primitives/button'; @@ -25,6 +25,8 @@ import { Input } from '@documenso/ui/primitives/input'; import { PasswordInput } from '@documenso/ui/primitives/password-input'; import { useToast } from '@documenso/ui/primitives/use-toast'; +import { signupErrorMessages } from '~/components/forms/v2/signup'; + export type ClaimAccountProps = { defaultName: string; defaultEmail: string; @@ -33,7 +35,10 @@ export type ClaimAccountProps = { export const ZClaimAccountFormSchema = z .object({ - name: z.string().trim().min(1, { message: 'Please enter a valid name.' }), + name: z + .string() + .trim() + .min(1, { message: msg`Please enter a valid name.`.id }), email: z.string().email().min(1), password: ZPasswordSchema, }) @@ -43,7 +48,7 @@ export const ZClaimAccountFormSchema = z return !password.includes(name) && !password.includes(email.split('@')[0]); }, { - message: 'Password should not be common or based on personal information', + message: msg`Password should not be common or based on personal information`.id, path: ['password'], }, ); @@ -86,22 +91,16 @@ export const ClaimAccount = ({ defaultName, defaultEmail }: ClaimAccountProps) = email, timestamp: new Date().toISOString(), }); - } catch (error) { - if (error instanceof TRPCClientError && error.data?.code === 'BAD_REQUEST') { - toast({ - title: _(msg`An error occurred`), - description: error.message, - variant: 'destructive', - }); - } else { - toast({ - title: _(msg`An unknown error occurred`), - description: _( - msg`We encountered an unknown error while attempting to sign you up. Please try again later.`, - ), - variant: 'destructive', - }); - } + } catch (err) { + const error = AppError.parseError(err); + + const errorMessage = signupErrorMessages[error.code] ?? signupErrorMessages.INVALID_REQUEST; + + toast({ + title: _(msg`An error occurred`), + description: _(errorMessage), + variant: 'destructive', + }); } }; diff --git a/apps/web/src/components/forms/avatar-image.tsx b/apps/web/src/components/forms/avatar-image.tsx index e253a0feb..64e6264c0 100644 --- a/apps/web/src/components/forms/avatar-image.tsx +++ b/apps/web/src/components/forms/avatar-image.tsx @@ -13,10 +13,10 @@ import { match } from 'ts-pattern'; import { z } from 'zod'; import { NEXT_PUBLIC_WEBAPP_URL } from '@documenso/lib/constants/app'; +import { AppError } from '@documenso/lib/errors/app-error'; import { base64 } from '@documenso/lib/universal/base64'; import { extractInitials } from '@documenso/lib/utils/recipient-formatter'; import type { Team, User } from '@documenso/prisma/client'; -import { TRPCClientError } from '@documenso/trpc/client'; import { trpc } from '@documenso/trpc/react'; import { cn } from '@documenso/ui/lib/utils'; import { Avatar, AvatarFallback, AvatarImage } from '@documenso/ui/primitives/avatar'; @@ -111,21 +111,18 @@ export const AvatarImageForm = ({ className, user, team }: AvatarImageFormProps) router.refresh(); } catch (err) { - if (err instanceof TRPCClientError && err.data?.code === 'BAD_REQUEST') { - toast({ - title: _(msg`An error occurred`), - description: err.message, - variant: 'destructive', - }); - } else { - toast({ - title: _(msg`An unknown error occurred`), - description: _( - msg`We encountered an unknown error while attempting to update the avatar. Please try again later.`, - ), - variant: 'destructive', - }); - } + const error = AppError.parseError(err); + + const errorMessage = match(error.code).otherwise( + () => + msg`We encountered an unknown error while attempting to update your password. Please try again later.`, + ); + + toast({ + title: _(msg`An error occurred`), + description: _(errorMessage), + variant: 'destructive', + }); } }; diff --git a/apps/web/src/components/forms/password.tsx b/apps/web/src/components/forms/password.tsx index 3ff3e18df..c77373972 100644 --- a/apps/web/src/components/forms/password.tsx +++ b/apps/web/src/components/forms/password.tsx @@ -4,10 +4,11 @@ import { zodResolver } from '@hookform/resolvers/zod'; import { Trans, msg } from '@lingui/macro'; import { useLingui } from '@lingui/react'; import { useForm } from 'react-hook-form'; +import { match } from 'ts-pattern'; import { z } from 'zod'; +import { AppError } from '@documenso/lib/errors/app-error'; import type { User } from '@documenso/prisma/client'; -import { TRPCClientError } from '@documenso/trpc/client'; import { trpc } from '@documenso/trpc/react'; import { ZCurrentPasswordSchema, ZPasswordSchema } from '@documenso/trpc/server/auth-router/schema'; import { cn } from '@documenso/ui/lib/utils'; @@ -73,21 +74,25 @@ export const PasswordForm = ({ className }: PasswordFormProps) => { duration: 5000, }); } catch (err) { - if (err instanceof TRPCClientError && err.data?.code === 'BAD_REQUEST') { - toast({ - title: _(msg`An error occurred`), - description: err.message, - variant: 'destructive', - }); - } else { - toast({ - title: _(msg`An unknown error occurred`), - description: _( + const error = AppError.parseError(err); + + const errorMessage = match(error.code) + .with('NO_PASSWORD', () => msg`User has no password.`) + .with('INCORRECT_PASSWORD', () => msg`Current password is incorrect.`) + .with( + 'SAME_PASSWORD', + () => msg`Your new password cannot be the same as your old password.`, + ) + .otherwise( + () => msg`We encountered an unknown error while attempting to update your password. Please try again later.`, - ), - variant: 'destructive', - }); - } + ); + + toast({ + title: _(msg`An error occurred`), + description: _(errorMessage), + variant: 'destructive', + }); } }; diff --git a/apps/web/src/components/forms/reset-password.tsx b/apps/web/src/components/forms/reset-password.tsx index 9fb2002d8..fb8580d96 100644 --- a/apps/web/src/components/forms/reset-password.tsx +++ b/apps/web/src/components/forms/reset-password.tsx @@ -6,9 +6,10 @@ import { zodResolver } from '@hookform/resolvers/zod'; import { Trans, msg } from '@lingui/macro'; import { useLingui } from '@lingui/react'; import { useForm } from 'react-hook-form'; +import { match } from 'ts-pattern'; import { z } from 'zod'; -import { TRPCClientError } from '@documenso/trpc/client'; +import { AppError, AppErrorCode } from '@documenso/lib/errors/app-error'; import { trpc } from '@documenso/trpc/react'; import { ZPasswordSchema } from '@documenso/trpc/server/auth-router/schema'; import { cn } from '@documenso/ui/lib/utils'; @@ -76,21 +77,25 @@ export const ResetPasswordForm = ({ className, token }: ResetPasswordFormProps) router.push('/signin'); } catch (err) { - if (err instanceof TRPCClientError && err.data?.code === 'BAD_REQUEST') { - toast({ - title: _(msg`An error occurred`), - description: err.message, - variant: 'destructive', - }); - } else { - toast({ - title: _(msg`An unknown error occurred`), - description: _( + const error = AppError.parseError(err); + + const errorMessage = match(error.code) + .with(AppErrorCode.EXPIRED_CODE, () => msg`Token has expired. Please try again.`) + .with('INVALID_TOKEN', () => msg`Invalid token provided. Please try again.`) + .with( + 'SAME_PASSWORD', + () => msg`Your new password cannot be the same as your old password.`, + ) + .otherwise( + () => msg`We encountered an unknown error while attempting to reset your password. Please try again later.`, - ), - variant: 'destructive', - }); - } + ); + + toast({ + title: _(msg`An error occurred`), + description: _(errorMessage), + variant: 'destructive', + }); } }; diff --git a/apps/web/src/components/forms/signup.tsx b/apps/web/src/components/forms/signup.tsx index df7d24e85..762db4cef 100644 --- a/apps/web/src/components/forms/signup.tsx +++ b/apps/web/src/components/forms/signup.tsx @@ -11,7 +11,7 @@ import { FcGoogle } from 'react-icons/fc'; import { z } from 'zod'; import { useAnalytics } from '@documenso/lib/client-only/hooks/use-analytics'; -import { TRPCClientError } from '@documenso/trpc/client'; +import { AppError } from '@documenso/lib/errors/app-error'; import { trpc } from '@documenso/trpc/react'; import { ZPasswordSchema } from '@documenso/trpc/server/auth-router/schema'; import { cn } from '@documenso/ui/lib/utils'; @@ -29,6 +29,8 @@ import { PasswordInput } from '@documenso/ui/primitives/password-input'; import { SignaturePad } from '@documenso/ui/primitives/signature-pad'; import { useToast } from '@documenso/ui/primitives/use-toast'; +import { signupErrorMessages } from './v2/signup'; + const SIGN_UP_REDIRECT_PATH = '/documents'; export const ZSignUpFormSchema = z @@ -102,21 +104,15 @@ export const SignUpForm = ({ timestamp: new Date().toISOString(), }); } catch (err) { - if (err instanceof TRPCClientError && err.data?.code === 'BAD_REQUEST') { - toast({ - title: _(msg`An error occurred`), - description: err.message, - variant: 'destructive', - }); - } else { - toast({ - title: _(msg`An unknown error occurred`), - description: _( - msg`We encountered an unknown error while attempting to sign you up. Please try again later.`, - ), - variant: 'destructive', - }); - } + const error = AppError.parseError(err); + + const errorMessage = signupErrorMessages[error.code] ?? signupErrorMessages.INVALID_REQUEST; + + toast({ + title: _(msg`An error occurred`), + description: _(errorMessage), + variant: 'destructive', + }); } }; diff --git a/apps/web/src/components/forms/v2/signup.tsx b/apps/web/src/components/forms/v2/signup.tsx index a9d85154c..7c7c5d4d8 100644 --- a/apps/web/src/components/forms/v2/signup.tsx +++ b/apps/web/src/components/forms/v2/signup.tsx @@ -7,6 +7,7 @@ import Link from 'next/link'; import { useRouter, useSearchParams } from 'next/navigation'; import { zodResolver } from '@hookform/resolvers/zod'; +import type { MessageDescriptor } from '@lingui/core'; import { Trans, msg } from '@lingui/macro'; import { useLingui } from '@lingui/react'; import { AnimatePresence, motion } from 'framer-motion'; @@ -20,7 +21,6 @@ import communityCardsImage from '@documenso/assets/images/community-cards.png'; import { useAnalytics } from '@documenso/lib/client-only/hooks/use-analytics'; import { NEXT_PUBLIC_WEBAPP_URL } from '@documenso/lib/constants/app'; import { AppError, AppErrorCode } from '@documenso/lib/errors/app-error'; -import { TRPCClientError } from '@documenso/trpc/client'; import { trpc } from '@documenso/trpc/react'; import { ZPasswordSchema } from '@documenso/trpc/server/auth-router/schema'; import { cn } from '@documenso/ui/lib/utils'; @@ -47,17 +47,20 @@ type SignUpStep = 'BASIC_DETAILS' | 'CLAIM_USERNAME'; export const ZSignUpFormV2Schema = z .object({ - name: z.string().trim().min(1, { message: 'Please enter a valid name.' }), + name: z + .string() + .trim() + .min(1, { message: msg`Please enter a valid name.`.id }), email: z.string().email().min(1), password: ZPasswordSchema, - signature: z.string().min(1, { message: 'We need your signature to sign documents' }), + signature: z.string().min(1, { message: msg`We need your signature to sign documents`.id }), url: z .string() .trim() .toLowerCase() - .min(1, { message: 'We need a username to create your profile' }) + .min(1, { message: msg`We need a username to create your profile`.id }) .regex(/^[a-z0-9-]+$/, { - message: 'Username can only container alphanumeric characters and dashes.', + message: msg`Username can only container alphanumeric characters and dashes.`.id, }), }) .refine( @@ -66,10 +69,18 @@ export const ZSignUpFormV2Schema = z return !password.includes(name) && !password.includes(email.split('@')[0]); }, { - message: 'Password should not be common or based on personal information', + message: msg`Password should not be common or based on personal information`.id, }, ); +export const signupErrorMessages: Record = { + SIGNUP_DISABLED: msg`Signups are disabled.`, + [AppErrorCode.ALREADY_EXISTS]: msg`User with this email already exists. Please use a different email address.`, + [AppErrorCode.INVALID_REQUEST]: msg`We were unable to create your account. Please review the information you provided and try again.`, + [AppErrorCode.PROFILE_URL_TAKEN]: msg`This username has already been taken`, + [AppErrorCode.PREMIUM_PROFILE_URL]: msg`Only subscribers can have a username shorter than 6 characters`, +}; + export type TSignUpFormV2Schema = z.infer; export type SignUpFormV2Props = { @@ -139,28 +150,20 @@ export const SignUpFormV2 = ({ } catch (err) { const error = AppError.parseError(err); - if (error.code === AppErrorCode.PROFILE_URL_TAKEN) { + const errorMessage = signupErrorMessages[error.code] ?? signupErrorMessages.INVALID_REQUEST; + + if ( + error.code === AppErrorCode.PROFILE_URL_TAKEN || + error.code === AppErrorCode.PREMIUM_PROFILE_URL + ) { form.setError('url', { type: 'manual', - message: _(msg`This username has already been taken`), - }); - } else if (error.code === AppErrorCode.PREMIUM_PROFILE_URL) { - form.setError('url', { - type: 'manual', - message: error.message, - }); - } else if (err instanceof TRPCClientError && err.data?.code === 'BAD_REQUEST') { - toast({ - title: _(msg`An error occurred`), - description: err.message, - variant: 'destructive', + message: _(errorMessage), }); } else { toast({ - title: _(msg`An unknown error occurred`), - description: _( - msg`We encountered an unknown error while attempting to sign you up. Please try again later.`, - ), + title: _(msg`An error occurred`), + description: _(errorMessage), variant: 'destructive', }); } diff --git a/apps/web/src/pages/api/trpc/[trpc].ts b/apps/web/src/pages/api/trpc/[trpc].ts index 5b44ea764..3857e75c0 100644 --- a/apps/web/src/pages/api/trpc/[trpc].ts +++ b/apps/web/src/pages/api/trpc/[trpc].ts @@ -21,8 +21,7 @@ export default trpcNext.createNextApiHandler({ onError(opts) { const { error, path } = opts; - // Currently trialing changes with template and team router only. - if (!path || (!path.startsWith('template') && !path.startsWith('team'))) { + if (!path) { return; } diff --git a/packages/lib/server-only/crypto/encrypt.ts b/packages/lib/server-only/crypto/encrypt.ts index 83de19cc2..6c913133d 100644 --- a/packages/lib/server-only/crypto/encrypt.ts +++ b/packages/lib/server-only/crypto/encrypt.ts @@ -2,7 +2,6 @@ import { z } from 'zod'; import { DOCUMENSO_ENCRYPTION_SECONDARY_KEY } from '@documenso/lib/constants/crypto'; import { symmetricEncrypt } from '@documenso/lib/universal/crypto'; -import type { TEncryptSecondaryDataMutationSchema } from '@documenso/trpc/server/crypto/schema'; export const ZEncryptedDataSchema = z.object({ data: z.string(), @@ -25,7 +24,7 @@ export type EncryptDataOptions = { * * @returns The encrypted data. */ -export const encryptSecondaryData = ({ data, expiresAt }: TEncryptSecondaryDataMutationSchema) => { +export const encryptSecondaryData = ({ data, expiresAt }: EncryptDataOptions) => { if (!DOCUMENSO_ENCRYPTION_SECONDARY_KEY) { throw new Error('Missing encryption key'); } diff --git a/packages/lib/server-only/profile/set-avatar-image.ts b/packages/lib/server-only/profile/set-avatar-image.ts index 79efda5bd..92683dcc4 100644 --- a/packages/lib/server-only/profile/set-avatar-image.ts +++ b/packages/lib/server-only/profile/set-avatar-image.ts @@ -2,6 +2,7 @@ import sharp from 'sharp'; import { prisma } from '@documenso/prisma'; +import { AppError, AppErrorCode } from '../../errors/app-error'; import type { RequestMetadata } from '../../universal/extract-request-metadata'; export type SetAvatarImageOptions = { @@ -29,7 +30,9 @@ export const setAvatarImage = async ({ }); if (!user) { - throw new Error('User not found'); + throw new AppError(AppErrorCode.NOT_FOUND, { + message: 'User not found', + }); } oldAvatarImageId = user.avatarImageId; @@ -47,7 +50,9 @@ export const setAvatarImage = async ({ }); if (!team) { - throw new Error('Team not found'); + throw new AppError('TEAM_NOT_FOUND', { + statusCode: 404, + }); } oldAvatarImageId = team.avatarImageId; diff --git a/packages/lib/server-only/user/create-user.ts b/packages/lib/server-only/user/create-user.ts index 742b906fe..a596b709d 100644 --- a/packages/lib/server-only/user/create-user.ts +++ b/packages/lib/server-only/user/create-user.ts @@ -8,6 +8,7 @@ import { IdentityProvider, TeamMemberInviteStatus } from '@documenso/prisma/clie import { IS_BILLING_ENABLED } from '../../constants/app'; import { SALT_ROUNDS } from '../../constants/auth'; import { AppError, AppErrorCode } from '../../errors/app-error'; +import { buildLogger } from '../../utils/logger'; export interface CreateUserOptions { name: string; @@ -27,7 +28,7 @@ export const createUser = async ({ name, email, password, signature, url }: Crea }); if (userExists) { - throw new Error('User already exists'); + throw new AppError(AppErrorCode.ALREADY_EXISTS); } if (url) { @@ -134,6 +135,18 @@ export const createUser = async ({ name, email, password, signature, url }: Crea return await getStripeCustomerByUser(user).then((session) => session.user); } catch (err) { console.error(err); + + const error = AppError.parseError(err); + + const logger = buildLogger(); + + logger.error(error, { + method: 'createUser', + context: { + appError: AppError.toJSON(error), + userId: user.id, + }, + }); } } diff --git a/packages/lib/server-only/user/reset-password.ts b/packages/lib/server-only/user/reset-password.ts index 457d7e28c..823c645b5 100644 --- a/packages/lib/server-only/user/reset-password.ts +++ b/packages/lib/server-only/user/reset-password.ts @@ -4,6 +4,7 @@ import { prisma } from '@documenso/prisma'; import { UserSecurityAuditLogType } from '@documenso/prisma/client'; import { SALT_ROUNDS } from '../../constants/auth'; +import { AppError, AppErrorCode } from '../../errors/app-error'; import type { RequestMetadata } from '../../universal/extract-request-metadata'; import { sendResetPassword } from '../auth/send-reset-password'; @@ -15,7 +16,7 @@ export type ResetPasswordOptions = { export const resetPassword = async ({ token, password, requestMetadata }: ResetPasswordOptions) => { if (!token) { - throw new Error('Invalid token provided. Please try again.'); + throw new AppError('INVALID_TOKEN'); } const foundToken = await prisma.passwordResetToken.findFirst({ @@ -28,19 +29,19 @@ export const resetPassword = async ({ token, password, requestMetadata }: ResetP }); if (!foundToken) { - throw new Error('Invalid token provided. Please try again.'); + throw new AppError('INVALID_TOKEN'); } const now = new Date(); if (now > foundToken.expiry) { - throw new Error('Token has expired. Please try again.'); + throw new AppError(AppErrorCode.EXPIRED_CODE); } const isSamePassword = await compare(password, foundToken.User.password || ''); if (isSamePassword) { - throw new Error('Your new password cannot be the same as your old password.'); + throw new AppError('SAME_PASSWORD'); } const hashedPassword = await hash(password, SALT_ROUNDS); diff --git a/packages/lib/server-only/user/update-password.ts b/packages/lib/server-only/user/update-password.ts index 9c4882c5a..c8f55574e 100644 --- a/packages/lib/server-only/user/update-password.ts +++ b/packages/lib/server-only/user/update-password.ts @@ -5,6 +5,8 @@ import type { RequestMetadata } from '@documenso/lib/universal/extract-request-m import { prisma } from '@documenso/prisma'; import { UserSecurityAuditLogType } from '@documenso/prisma/client'; +import { AppError } from '../../errors/app-error'; + export type UpdatePasswordOptions = { userId: number; password: string; @@ -26,18 +28,18 @@ export const updatePassword = async ({ }); if (!user.password) { - throw new Error('User has no password'); + throw new AppError('NO_PASSWORD'); } const isCurrentPasswordValid = await compare(currentPassword, user.password); if (!isCurrentPasswordValid) { - throw new Error('Current password is incorrect.'); + throw new AppError('INCORRECT_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.'); + throw new AppError('SAME_PASSWORD'); } const hashedNewPassword = await hash(password, SALT_ROUNDS); diff --git a/packages/trpc/server/admin-router/router.ts b/packages/trpc/server/admin-router/router.ts index 7c211ce82..593366055 100644 --- a/packages/trpc/server/admin-router/router.ts +++ b/packages/trpc/server/admin-router/router.ts @@ -1,5 +1,3 @@ -import { TRPCError } from '@trpc/server'; - import { findDocuments } from '@documenso/lib/server-only/admin/get-all-documents'; import { getEntireDocument } from '@documenso/lib/server-only/admin/get-entire-document'; import { updateRecipient } from '@documenso/lib/server-only/admin/update-recipient'; @@ -28,16 +26,7 @@ export const adminRouter = router({ findDocuments: adminProcedure.input(ZAdminFindDocumentsQuerySchema).query(async ({ input }) => { const { term, page, perPage } = input; - try { - return await findDocuments({ term, page, perPage }); - } catch (err) { - console.error(err); - - throw new TRPCError({ - code: 'BAD_REQUEST', - message: 'We were unable to retrieve the documents. Please try again.', - }); - } + return await findDocuments({ term, page, perPage }); }), updateUser: adminProcedure @@ -45,16 +34,7 @@ export const adminRouter = router({ .mutation(async ({ input }) => { const { id, name, email, roles } = input; - try { - return await updateUser({ id, name, email, roles }); - } catch (err) { - console.error(err); - - throw new TRPCError({ - code: 'BAD_REQUEST', - message: 'We were unable to retrieve the specified account. Please try again.', - }); - } + return await updateUser({ id, name, email, roles }); }), updateRecipient: adminProcedure @@ -62,38 +42,20 @@ export const adminRouter = router({ .mutation(async ({ input }) => { const { id, name, email } = input; - try { - return await updateRecipient({ id, name, email }); - } catch (err) { - console.error(err); - - throw new TRPCError({ - code: 'BAD_REQUEST', - message: 'We were unable to update the recipient provided.', - }); - } + return await updateRecipient({ id, name, email }); }), updateSiteSetting: adminProcedure .input(ZAdminUpdateSiteSettingMutationSchema) .mutation(async ({ ctx, input }) => { - try { - const { id, enabled, data } = input; + const { id, enabled, data } = input; - return await upsertSiteSetting({ - id, - enabled, - data, - userId: ctx.user.id, - }); - } catch (err) { - console.error(err); - - throw new TRPCError({ - code: 'BAD_REQUEST', - message: 'We were unable to update the site setting provided.', - }); - } + return await upsertSiteSetting({ + id, + enabled, + data, + userId: ctx.user.id, + }); }), resealDocument: adminProcedure @@ -101,61 +63,34 @@ export const adminRouter = router({ .mutation(async ({ input }) => { const { id } = input; - try { - const document = await getEntireDocument({ id }); + const document = await getEntireDocument({ id }); - const isResealing = document.status === DocumentStatus.COMPLETED; + const isResealing = document.status === DocumentStatus.COMPLETED; - return await sealDocument({ documentId: id, isResealing }); - } catch (err) { - console.error('resealDocument error', err); - - throw new TRPCError({ - code: 'BAD_REQUEST', - message: 'We were unable to reseal the document provided.', - }); - } + return await sealDocument({ documentId: id, isResealing }); }), deleteUser: adminProcedure.input(ZAdminDeleteUserMutationSchema).mutation(async ({ input }) => { const { id, email } = input; - try { - const user = await getUserById({ id }); + const user = await getUserById({ id }); - if (user.email !== email) { - throw new Error('Email does not match'); - } - - return await deleteUser({ id }); - } catch (err) { - console.error(err); - - throw new TRPCError({ - code: 'BAD_REQUEST', - message: 'We were unable to delete the specified account. Please try again.', - }); + if (user.email !== email) { + throw new Error('Email does not match'); } + + return await deleteUser({ id }); }), deleteDocument: adminProcedure .input(ZAdminDeleteDocumentMutationSchema) .mutation(async ({ ctx, input }) => { const { id, reason } = input; - try { - await sendDeleteEmail({ documentId: id, reason }); + await sendDeleteEmail({ documentId: id, reason }); - return await superDeleteDocument({ - id, - requestMetadata: extractNextApiRequestMetadata(ctx.req), - }); - } catch (err) { - console.error(err); - - throw new TRPCError({ - code: 'BAD_REQUEST', - message: 'We were unable to delete the specified document. Please try again.', - }); - } + return await superDeleteDocument({ + id, + requestMetadata: extractNextApiRequestMetadata(ctx.req), + }); }), }); diff --git a/packages/trpc/server/api-token-router/router.ts b/packages/trpc/server/api-token-router/router.ts index 55129f029..eae15f1e3 100644 --- a/packages/trpc/server/api-token-router/router.ts +++ b/packages/trpc/server/api-token-router/router.ts @@ -1,5 +1,3 @@ -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'; @@ -14,78 +12,42 @@ import { export const apiTokenRouter = router({ getTokens: authenticatedProcedure.query(async ({ ctx }) => { - try { - return await getUserTokens({ userId: ctx.user.id }); - } catch (err) { - console.error(err); - - throw new TRPCError({ - code: 'BAD_REQUEST', - message: 'We were unable to find your API tokens. Please try again.', - }); - } + return await getUserTokens({ userId: ctx.user.id }); }), getTokenById: authenticatedProcedure .input(ZGetApiTokenByIdQuerySchema) .query(async ({ input, ctx }) => { - try { - const { id } = input; + const { id } = input; - return await getApiTokenById({ - id, - userId: ctx.user.id, - }); - } catch (err) { - console.error(err); - - throw new TRPCError({ - code: 'BAD_REQUEST', - message: 'We were unable to find this API token. Please try again.', - }); - } + return await getApiTokenById({ + id, + userId: ctx.user.id, + }); }), createToken: authenticatedProcedure .input(ZCreateTokenMutationSchema) .mutation(async ({ input, ctx }) => { - try { - const { tokenName, teamId, expirationDate } = input; + const { tokenName, teamId, expirationDate } = input; - return await createApiToken({ - userId: ctx.user.id, - teamId, - tokenName, - expiresIn: expirationDate, - }); - } catch (err) { - console.error(err); - - throw new TRPCError({ - code: 'BAD_REQUEST', - message: 'We were unable to create an API token. Please try again.', - }); - } + return await createApiToken({ + userId: ctx.user.id, + teamId, + tokenName, + expiresIn: expirationDate, + }); }), deleteTokenById: authenticatedProcedure .input(ZDeleteTokenByIdMutationSchema) .mutation(async ({ input, ctx }) => { - try { - const { id, teamId } = input; + const { id, teamId } = input; - return await deleteTokenById({ - id, - teamId, - userId: ctx.user.id, - }); - } catch (err) { - console.error(err); - - throw new TRPCError({ - code: 'BAD_REQUEST', - message: 'We were unable to delete this API Token. Please try again.', - }); - } + return await deleteTokenById({ + id, + teamId, + userId: ctx.user.id, + }); }), }); diff --git a/packages/trpc/server/auth-router/router.ts b/packages/trpc/server/auth-router/router.ts index bc4b89d3e..4d2807a3a 100644 --- a/packages/trpc/server/auth-router/router.ts +++ b/packages/trpc/server/auth-router/router.ts @@ -33,53 +33,30 @@ const NEXT_PUBLIC_DISABLE_SIGNUP = () => env('NEXT_PUBLIC_DISABLE_SIGNUP'); export const authRouter = router({ signup: procedure.input(ZSignUpMutationSchema).mutation(async ({ input }) => { - try { - if (NEXT_PUBLIC_DISABLE_SIGNUP() === 'true') { - throw new TRPCError({ - code: 'BAD_REQUEST', - message: 'Signups are disabled.', - }); - } - - const { name, email, password, signature, url } = input; - - if (IS_BILLING_ENABLED() && url && url.length < 6) { - throw new AppError(AppErrorCode.PREMIUM_PROFILE_URL, { - message: 'Only subscribers can have a username shorter than 6 characters', - }); - } - - const user = await createUser({ name, email, password, signature, url }); - - await jobsClient.triggerJob({ - name: 'send.signup.confirmation.email', - payload: { - email: user.email, - }, - }); - - return user; - } catch (err) { - console.error(err); - - const error = AppError.parseError(err); - - if (error.code !== AppErrorCode.UNKNOWN_ERROR) { - throw error; - } - - let message = - 'We were unable to create your account. Please review the information you provided and try again.'; - - if (err instanceof Error && err.message === 'User already exists') { - message = 'User with this email already exists. Please use a different email address.'; - } - - throw new TRPCError({ - code: 'BAD_REQUEST', - message, + if (NEXT_PUBLIC_DISABLE_SIGNUP() === 'true') { + throw new AppError('SIGNUP_DISABLED', { + message: 'Signups are disabled.', }); } + + const { name, email, password, signature, url } = input; + + if (IS_BILLING_ENABLED() && url && url.length < 6) { + throw new AppError(AppErrorCode.PREMIUM_PROFILE_URL, { + message: 'Only subscribers can have a username shorter than 6 characters', + }); + } + + const user = await createUser({ name, email, password, signature, url }); + + await jobsClient.triggerJob({ + name: 'send.signup.confirmation.email', + payload: { + email: user.email, + }, + }); + + return user; }), verifyPassword: authenticatedProcedure @@ -104,56 +81,30 @@ export const authRouter = router({ createPasskey: authenticatedProcedure .input(ZCreatePasskeyMutationSchema) .mutation(async ({ ctx, input }) => { - try { - // eslint-disable-next-line @typescript-eslint/consistent-type-assertions - const verificationResponse = input.verificationResponse as RegistrationResponseJSON; + // eslint-disable-next-line @typescript-eslint/consistent-type-assertions + const verificationResponse = input.verificationResponse as RegistrationResponseJSON; - return await createPasskey({ - userId: ctx.user.id, - verificationResponse, - passkeyName: input.passkeyName, - requestMetadata: extractNextApiRequestMetadata(ctx.req), - }); - } catch (err) { - console.error(err); - - throw err; - } + return await createPasskey({ + userId: ctx.user.id, + verificationResponse, + passkeyName: input.passkeyName, + requestMetadata: extractNextApiRequestMetadata(ctx.req), + }); }), createPasskeyAuthenticationOptions: authenticatedProcedure .input(ZCreatePasskeyAuthenticationOptionsMutationSchema) .mutation(async ({ ctx, input }) => { - try { - return await createPasskeyAuthenticationOptions({ - userId: ctx.user.id, - preferredPasskeyId: input?.preferredPasskeyId, - }); - } catch (err) { - console.error(err); - - throw new TRPCError({ - code: 'BAD_REQUEST', - message: - 'We were unable to create the authentication options for the passkey. Please try again later.', - }); - } + return await createPasskeyAuthenticationOptions({ + userId: ctx.user.id, + preferredPasskeyId: input?.preferredPasskeyId, + }); }), createPasskeyRegistrationOptions: authenticatedProcedure.mutation(async ({ ctx }) => { - try { - return await createPasskeyRegistrationOptions({ - userId: ctx.user.id, - }); - } catch (err) { - console.error(err); - - throw new TRPCError({ - code: 'BAD_REQUEST', - message: - 'We were unable to create the registration options for the passkey. Please try again later.', - }); - } + return await createPasskeyRegistrationOptions({ + userId: ctx.user.id, + }); }), createPasskeySigninOptions: procedure.mutation(async ({ ctx }) => { @@ -168,80 +119,44 @@ export const authRouter = router({ const [sessionId] = decodeURI(sessionIdToken).split('|'); - try { - return await createPasskeySigninOptions({ sessionId }); - } catch (err) { - console.error(err); - - throw new TRPCError({ - code: 'BAD_REQUEST', - message: 'We were unable to create the options for passkey signin. Please try again later.', - }); - } + return await createPasskeySigninOptions({ sessionId }); }), deletePasskey: authenticatedProcedure .input(ZDeletePasskeyMutationSchema) .mutation(async ({ ctx, input }) => { - try { - const { passkeyId } = input; + const { passkeyId } = input; - await deletePasskey({ - userId: ctx.user.id, - passkeyId, - requestMetadata: extractNextApiRequestMetadata(ctx.req), - }); - } catch (err) { - console.error(err); - - throw new TRPCError({ - code: 'BAD_REQUEST', - message: 'We were unable to delete this passkey. Please try again later.', - }); - } + await deletePasskey({ + userId: ctx.user.id, + passkeyId, + requestMetadata: extractNextApiRequestMetadata(ctx.req), + }); }), findPasskeys: authenticatedProcedure .input(ZFindPasskeysQuerySchema) .query(async ({ input, ctx }) => { - try { - const { page, perPage, orderBy } = input; + const { page, perPage, orderBy } = input; - return await findPasskeys({ - page, - perPage, - orderBy, - userId: ctx.user.id, - }); - } catch (err) { - console.error(err); - - throw new TRPCError({ - code: 'BAD_REQUEST', - message: 'We were unable to find passkeys. Please try again later.', - }); - } + return await findPasskeys({ + page, + perPage, + orderBy, + userId: ctx.user.id, + }); }), updatePasskey: authenticatedProcedure .input(ZUpdatePasskeyMutationSchema) .mutation(async ({ ctx, input }) => { - try { - const { passkeyId, name } = input; + const { passkeyId, name } = input; - await updatePasskey({ - userId: ctx.user.id, - passkeyId, - name, - requestMetadata: extractNextApiRequestMetadata(ctx.req), - }); - } catch (err) { - console.error(err); - - throw new TRPCError({ - code: 'BAD_REQUEST', - message: 'We were unable to update this passkey. Please try again later.', - }); - } + await updatePasskey({ + userId: ctx.user.id, + passkeyId, + name, + requestMetadata: extractNextApiRequestMetadata(ctx.req), + }); }), }); diff --git a/packages/trpc/server/crypto/router.ts b/packages/trpc/server/crypto/router.ts deleted file mode 100644 index 4557a8331..000000000 --- a/packages/trpc/server/crypto/router.ts +++ /dev/null @@ -1,8 +0,0 @@ -import { procedure, router } from '../trpc'; -import { ZEncryptSecondaryDataMutationSchema } from './schema'; - -export const cryptoRouter = router({ - encryptSecondaryData: procedure.input(ZEncryptSecondaryDataMutationSchema).mutation(() => { - throw new Error('Public usage of encryptSecondaryData is no longer permitted'); - }), -}); diff --git a/packages/trpc/server/crypto/schema.ts b/packages/trpc/server/crypto/schema.ts deleted file mode 100644 index ee4b49d53..000000000 --- a/packages/trpc/server/crypto/schema.ts +++ /dev/null @@ -1,15 +0,0 @@ -import { z } from 'zod'; - -export const ZEncryptSecondaryDataMutationSchema = z.object({ - data: z.string(), - expiresAt: z.number().optional(), -}); - -export const ZDecryptDataMutationSchema = z.object({ - data: z.string(), -}); - -export type TEncryptSecondaryDataMutationSchema = z.infer< - typeof ZEncryptSecondaryDataMutationSchema ->; -export type TDecryptDataMutationSchema = z.infer; diff --git a/packages/trpc/server/document-router/router.ts b/packages/trpc/server/document-router/router.ts index 14e8c8eb7..1372c6c03 100644 --- a/packages/trpc/server/document-router/router.ts +++ b/packages/trpc/server/document-router/router.ts @@ -51,145 +51,82 @@ export const documentRouter = router({ getDocumentById: authenticatedProcedure .input(ZGetDocumentByIdQuerySchema) .query(async ({ input, ctx }) => { - try { - return await getDocumentById({ - ...input, - userId: ctx.user.id, - }); - } catch (err) { - console.error(err); - - throw new TRPCError({ - code: 'BAD_REQUEST', - message: 'We were unable to find this document. Please try again later.', - }); - } + return await getDocumentById({ + ...input, + userId: ctx.user.id, + }); }), getDocumentByToken: procedure .input(ZGetDocumentByTokenQuerySchema) .query(async ({ input, ctx }) => { - try { - const { token } = input; + const { token } = input; - return await getDocumentAndSenderByToken({ - token, - userId: ctx.user?.id, - }); - } catch (err) { - console.error(err); - - throw new TRPCError({ - code: 'BAD_REQUEST', - message: 'We were unable to find this document. Please try again later.', - }); - } + return await getDocumentAndSenderByToken({ + token, + userId: ctx.user?.id, + }); }), getDocumentWithDetailsById: authenticatedProcedure .input(ZGetDocumentWithDetailsByIdQuerySchema) .query(async ({ input, ctx }) => { - try { - return await getDocumentWithDetailsById({ - ...input, - userId: ctx.user.id, - }); - } catch (err) { - console.error(err); - - throw new TRPCError({ - code: 'BAD_REQUEST', - message: 'We were unable to find this document. Please try again later.', - }); - } + return await getDocumentWithDetailsById({ + ...input, + userId: ctx.user.id, + }); }), createDocument: authenticatedProcedure .input(ZCreateDocumentMutationSchema) .mutation(async ({ input, ctx }) => { - try { - const { title, documentDataId, teamId } = input; + const { title, documentDataId, teamId } = input; - const { remaining } = await getServerLimits({ email: ctx.user.email, teamId }); - - if (remaining.documents <= 0) { - throw new TRPCError({ - code: 'BAD_REQUEST', - message: - 'You have reached your document limit for this month. Please upgrade your plan.', - }); - } - - return await createDocument({ - userId: ctx.user.id, - teamId, - title, - documentDataId, - requestMetadata: extractNextApiRequestMetadata(ctx.req), - }); - } catch (err) { - console.error(err); - - if (err instanceof TRPCError) { - throw err; - } + const { remaining } = await getServerLimits({ email: ctx.user.email, teamId }); + if (remaining.documents <= 0) { throw new TRPCError({ code: 'BAD_REQUEST', - message: 'We were unable to create this document. Please try again later.', + message: 'You have reached your document limit for this month. Please upgrade your plan.', }); } + + return await createDocument({ + userId: ctx.user.id, + teamId, + title, + documentDataId, + requestMetadata: extractNextApiRequestMetadata(ctx.req), + }); }), deleteDocument: authenticatedProcedure .input(ZDeleteDocumentMutationSchema) .mutation(async ({ input, ctx }) => { - try { - const { id, teamId } = input; + const { id, teamId } = input; - const userId = ctx.user.id; + const userId = ctx.user.id; - return await deleteDocument({ - id, - userId, - teamId, - requestMetadata: extractNextApiRequestMetadata(ctx.req), - }); - } catch (err) { - console.error(err); - - throw new TRPCError({ - code: 'BAD_REQUEST', - message: 'We were unable to delete this document. Please try again later.', - }); - } + return await deleteDocument({ + id, + userId, + teamId, + requestMetadata: extractNextApiRequestMetadata(ctx.req), + }); }), moveDocumentToTeam: authenticatedProcedure .input(ZMoveDocumentsToTeamSchema) .mutation(async ({ input, ctx }) => { - try { - const { documentId, teamId } = input; - const userId = ctx.user.id; + const { documentId, teamId } = input; + const userId = ctx.user.id; - return await moveDocumentToTeam({ - documentId, - teamId, - userId, - requestMetadata: extractNextApiRequestMetadata(ctx.req), - }); - } catch (err) { - console.error(err); - - if (err instanceof TRPCError) { - throw err; - } - - throw new TRPCError({ - code: 'BAD_REQUEST', - message: 'We were unable to move this document. Please try again later.', - }); - } + return await moveDocumentToTeam({ + documentId, + teamId, + userId, + requestMetadata: extractNextApiRequestMetadata(ctx.req), + }); }), findDocuments: authenticatedProcedure @@ -199,94 +136,66 @@ export const documentRouter = router({ const { search, teamId, templateId, page, perPage, orderBy, source, status } = input; - try { - const documents = await findDocuments({ - userId: user.id, - teamId, - templateId, - search, - source, - status, - page, - perPage, - orderBy, - }); + const documents = await findDocuments({ + userId: user.id, + teamId, + templateId, + search, + source, + status, + page, + perPage, + orderBy, + }); - return documents; - } catch (err) { - console.error(err); - - throw new TRPCError({ - code: 'BAD_REQUEST', - message: 'We are unable to search for documents. Please try again later.', - }); - } + return documents; }), findDocumentAuditLogs: authenticatedProcedure .input(ZFindDocumentAuditLogsQuerySchema) .query(async ({ input, ctx }) => { - try { - const { page, perPage, documentId, cursor, filterForRecentActivity, orderBy } = input; + 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.', - }); - } + return await findDocumentAuditLogs({ + page, + perPage, + documentId, + cursor, + filterForRecentActivity, + orderBy, + userId: ctx.user.id, + }); }), // Todo: Add API setSettingsForDocument: authenticatedProcedure .input(ZSetSettingsForDocumentMutationSchema) .mutation(async ({ input, ctx }) => { - try { - const { documentId, teamId, data, meta } = input; + const { documentId, teamId, data, meta } = input; - const userId = ctx.user.id; + const userId = ctx.user.id; - const requestMetadata = extractNextApiRequestMetadata(ctx.req); + const requestMetadata = extractNextApiRequestMetadata(ctx.req); - if (meta.timezone || meta.dateFormat || meta.redirectUrl) { - await upsertDocumentMeta({ - documentId, - dateFormat: meta.dateFormat, - timezone: meta.timezone, - redirectUrl: meta.redirectUrl, - language: meta.language, - userId: ctx.user.id, - requestMetadata, - }); - } - - return await updateDocumentSettings({ - userId, - teamId, + if (meta.timezone || meta.dateFormat || meta.redirectUrl) { + await upsertDocumentMeta({ documentId, - data, + dateFormat: meta.dateFormat, + timezone: meta.timezone, + redirectUrl: meta.redirectUrl, + language: meta.language, + userId: ctx.user.id, requestMetadata, }); - } catch (err) { - console.error(err); - - throw new TRPCError({ - code: 'BAD_REQUEST', - message: - 'We were unable to update the settings for this document. Please try again later.', - }); } + + return await updateDocumentSettings({ + userId, + teamId, + documentId, + data, + requestMetadata, + }); }), setTitleForDocument: authenticatedProcedure @@ -296,197 +205,131 @@ export const documentRouter = router({ const userId = ctx.user.id; - try { - return await updateTitle({ - title, - userId, - teamId, - documentId, - requestMetadata: extractNextApiRequestMetadata(ctx.req), - }); - } catch (err) { - console.error(err); - - throw err; - } + return await updateTitle({ + title, + userId, + teamId, + documentId, + requestMetadata: extractNextApiRequestMetadata(ctx.req), + }); }), setPasswordForDocument: authenticatedProcedure .input(ZSetPasswordForDocumentMutationSchema) .mutation(async ({ input, ctx }) => { - try { - const { documentId, password } = input; + const { documentId, password } = input; - const key = DOCUMENSO_ENCRYPTION_KEY; + const key = DOCUMENSO_ENCRYPTION_KEY; - if (!key) { - throw new Error('Missing encryption key'); - } - - const securePassword = symmetricEncrypt({ - data: password, - key, - }); - - await upsertDocumentMeta({ - documentId, - password: securePassword, - userId: ctx.user.id, - requestMetadata: extractNextApiRequestMetadata(ctx.req), - }); - } catch (err) { - console.error(err); - - throw new TRPCError({ - code: 'BAD_REQUEST', - message: 'We were unable to set the password for this document. Please try again later.', - }); + if (!key) { + throw new Error('Missing encryption key'); } + + const securePassword = symmetricEncrypt({ + data: password, + key, + }); + + await upsertDocumentMeta({ + documentId, + password: securePassword, + userId: ctx.user.id, + requestMetadata: extractNextApiRequestMetadata(ctx.req), + }); }), setSigningOrderForDocument: authenticatedProcedure .input(ZSetSigningOrderForDocumentMutationSchema) .mutation(async ({ input, ctx }) => { - try { - const { documentId, signingOrder } = input; + const { documentId, signingOrder } = input; - return await upsertDocumentMeta({ - documentId, - signingOrder, - userId: ctx.user.id, - requestMetadata: extractNextApiRequestMetadata(ctx.req), - }); - } catch (err) { - console.error(err); - - throw new TRPCError({ - code: 'BAD_REQUEST', - message: - 'We were unable to update the settings for this document. Please try again later.', - }); - } + return await upsertDocumentMeta({ + documentId, + signingOrder, + userId: ctx.user.id, + requestMetadata: extractNextApiRequestMetadata(ctx.req), + }); }), updateTypedSignatureSettings: authenticatedProcedure .input(ZUpdateTypedSignatureSettingsMutationSchema) .mutation(async ({ input, ctx }) => { - try { - const { documentId, teamId, typedSignatureEnabled } = input; + const { documentId, teamId, typedSignatureEnabled } = input; - const document = await getDocumentById({ - id: documentId, - teamId, - userId: ctx.user.id, - }).catch(() => null); - - if (!document) { - throw new TRPCError({ - code: 'NOT_FOUND', - message: 'Document not found', - }); - } - - return await upsertDocumentMeta({ - documentId, - typedSignatureEnabled, - userId: ctx.user.id, - requestMetadata: extractNextApiRequestMetadata(ctx.req), - }); - } catch (err) { - console.error(err); - - if (err instanceof TRPCError) { - throw err; - } + const document = await getDocumentById({ + id: documentId, + teamId, + userId: ctx.user.id, + }).catch(() => null); + if (!document) { throw new TRPCError({ - code: 'BAD_REQUEST', - message: - 'We were unable to update the settings for this document. Please try again later.', + code: 'NOT_FOUND', + message: 'Document not found', }); } + + return await upsertDocumentMeta({ + documentId, + typedSignatureEnabled, + userId: ctx.user.id, + requestMetadata: extractNextApiRequestMetadata(ctx.req), + }); }), sendDocument: authenticatedProcedure .input(ZSendDocumentMutationSchema) .mutation(async ({ input, ctx }) => { - try { - const { documentId, teamId, meta } = input; + const { documentId, teamId, meta } = input; - if ( - meta.message || - meta.subject || - meta.timezone || - meta.dateFormat || - meta.redirectUrl || - meta.distributionMethod || - meta.emailSettings - ) { - await upsertDocumentMeta({ - documentId, - subject: meta.subject, - message: meta.message, - dateFormat: meta.dateFormat, - timezone: meta.timezone, - redirectUrl: meta.redirectUrl, - distributionMethod: meta.distributionMethod, - userId: ctx.user.id, - emailSettings: meta.emailSettings, - requestMetadata: extractNextApiRequestMetadata(ctx.req), - }); - } - - return await sendDocument({ - userId: ctx.user.id, + if ( + meta.message || + meta.subject || + meta.timezone || + meta.dateFormat || + meta.redirectUrl || + meta.distributionMethod || + meta.emailSettings + ) { + await upsertDocumentMeta({ documentId, - teamId, + subject: meta.subject, + message: meta.message, + dateFormat: meta.dateFormat, + timezone: meta.timezone, + redirectUrl: meta.redirectUrl, + distributionMethod: meta.distributionMethod, + userId: ctx.user.id, + emailSettings: meta.emailSettings, requestMetadata: extractNextApiRequestMetadata(ctx.req), }); - } catch (err) { - console.error(err); - - throw new TRPCError({ - code: 'BAD_REQUEST', - message: 'We were unable to send this document. Please try again later.', - }); } + + return await sendDocument({ + userId: ctx.user.id, + documentId, + teamId, + requestMetadata: extractNextApiRequestMetadata(ctx.req), + }); }), resendDocument: authenticatedProcedure .input(ZResendDocumentMutationSchema) .mutation(async ({ input, ctx }) => { - try { - return await resendDocument({ - userId: ctx.user.id, - ...input, - requestMetadata: extractNextApiRequestMetadata(ctx.req), - }); - } catch (err) { - console.error(err); - - throw new TRPCError({ - code: 'BAD_REQUEST', - message: 'We were unable to resend this document. Please try again later.', - }); - } + return await resendDocument({ + userId: ctx.user.id, + ...input, + requestMetadata: extractNextApiRequestMetadata(ctx.req), + }); }), duplicateDocument: authenticatedProcedure .input(ZGetDocumentByIdQuerySchema) .mutation(async ({ input, ctx }) => { - try { - return await duplicateDocumentById({ - userId: ctx.user.id, - ...input, - }); - } catch (err) { - console.log(err); - - throw new TRPCError({ - code: 'BAD_REQUEST', - message: 'We are unable to duplicate this document. Please try again later.', - }); - } + return await duplicateDocumentById({ + userId: ctx.user.id, + ...input, + }); }), searchDocuments: authenticatedProcedure @@ -494,93 +337,64 @@ export const documentRouter = router({ .query(async ({ input, ctx }) => { const { query } = input; - try { - const documents = await searchDocumentsWithKeyword({ - query, - userId: ctx.user.id, - }); + const documents = await searchDocumentsWithKeyword({ + query, + userId: ctx.user.id, + }); - return documents; - } catch (err) { - console.error(err); - - throw new TRPCError({ - code: 'BAD_REQUEST', - message: 'We are unable to search for documents. Please try again later.', - }); - } + return documents; }), downloadAuditLogs: authenticatedProcedure .input(ZDownloadAuditLogsMutationSchema) .mutation(async ({ input, ctx }) => { - try { - const { documentId, teamId } = input; + const { documentId, teamId } = input; - const document = await getDocumentById({ - id: documentId, - userId: ctx.user.id, - teamId, - }).catch(() => null); - - if (!document || (teamId && document.teamId !== teamId)) { - throw new TRPCError({ - code: 'FORBIDDEN', - message: 'You do not have access to this document.', - }); - } - - const encrypted = encryptSecondaryData({ - data: document.id.toString(), - expiresAt: DateTime.now().plus({ minutes: 5 }).toJSDate().valueOf(), - }); - - return { - url: `${NEXT_PUBLIC_WEBAPP_URL()}/__htmltopdf/audit-log?d=${encrypted}`, - }; - } catch (err) { - console.error(err); + const document = await getDocumentById({ + id: documentId, + userId: ctx.user.id, + teamId, + }).catch(() => null); + if (!document || (teamId && document.teamId !== teamId)) { throw new TRPCError({ - code: 'BAD_REQUEST', - message: - 'We were unable to download the audit logs for this document. Please try again later.', + code: 'FORBIDDEN', + message: 'You do not have access to this document.', }); } + + const encrypted = encryptSecondaryData({ + data: document.id.toString(), + expiresAt: DateTime.now().plus({ minutes: 5 }).toJSDate().valueOf(), + }); + + return { + url: `${NEXT_PUBLIC_WEBAPP_URL()}/__htmltopdf/audit-log?d=${encrypted}`, + }; }), downloadCertificate: authenticatedProcedure .input(ZDownloadCertificateMutationSchema) .mutation(async ({ input, ctx }) => { - try { - const { documentId, teamId } = input; + const { documentId, teamId } = input; - const document = await getDocumentById({ - id: documentId, - userId: ctx.user.id, - teamId, - }); + const document = await getDocumentById({ + id: documentId, + userId: ctx.user.id, + teamId, + }); - if (document.status !== DocumentStatus.COMPLETED) { - throw new AppError('DOCUMENT_NOT_COMPLETE'); - } - - const encrypted = encryptSecondaryData({ - data: document.id.toString(), - expiresAt: DateTime.now().plus({ minutes: 5 }).toJSDate().valueOf(), - }); - - return { - url: `${NEXT_PUBLIC_WEBAPP_URL()}/__htmltopdf/certificate?d=${encrypted}`, - }; - } catch (err) { - console.error(err); - - throw new TRPCError({ - code: 'BAD_REQUEST', - message: - 'We were unable to download the audit logs for this document. Please try again later.', - }); + if (document.status !== DocumentStatus.COMPLETED) { + throw new AppError('DOCUMENT_NOT_COMPLETE'); } + + const encrypted = encryptSecondaryData({ + data: document.id.toString(), + expiresAt: DateTime.now().plus({ minutes: 5 }).toJSDate().valueOf(), + }); + + return { + url: `${NEXT_PUBLIC_WEBAPP_URL()}/__htmltopdf/certificate?d=${encrypted}`, + }; }), }); diff --git a/packages/trpc/server/field-router/router.ts b/packages/trpc/server/field-router/router.ts index 1d50095ec..c8ecb8d50 100644 --- a/packages/trpc/server/field-router/router.ts +++ b/packages/trpc/server/field-router/router.ts @@ -1,5 +1,3 @@ -import { TRPCError } from '@trpc/server'; - import { getFieldById } from '@documenso/lib/server-only/field/get-field-by-id'; import { removeSignedFieldWithToken } from '@documenso/lib/server-only/field/remove-signed-field-with-token'; import { setFieldsForDocument } from '@documenso/lib/server-only/field/set-fields-for-document'; @@ -20,33 +18,24 @@ export const fieldRouter = router({ addFields: authenticatedProcedure .input(ZAddFieldsMutationSchema) .mutation(async ({ input, ctx }) => { - try { - const { documentId, fields } = input; + const { documentId, fields } = input; - return await setFieldsForDocument({ - documentId, - userId: ctx.user.id, - fields: fields.map((field) => ({ - id: field.nativeId, - signerEmail: field.signerEmail, - type: field.type, - pageNumber: field.pageNumber, - pageX: field.pageX, - pageY: field.pageY, - pageWidth: field.pageWidth, - pageHeight: field.pageHeight, - fieldMeta: field.fieldMeta, - })), - requestMetadata: extractNextApiRequestMetadata(ctx.req), - }); - } catch (err) { - console.error(err); - - throw new TRPCError({ - code: 'BAD_REQUEST', - message: 'We were unable to set this field. Please try again later.', - }); - } + return await setFieldsForDocument({ + documentId, + userId: ctx.user.id, + fields: fields.map((field) => ({ + id: field.nativeId, + signerEmail: field.signerEmail, + type: field.type, + pageNumber: field.pageNumber, + pageX: field.pageX, + pageY: field.pageY, + pageWidth: field.pageWidth, + pageHeight: field.pageHeight, + fieldMeta: field.fieldMeta, + })), + requestMetadata: extractNextApiRequestMetadata(ctx.req), + }); }), addTemplateFields: authenticatedProcedure @@ -54,89 +43,59 @@ export const fieldRouter = router({ .mutation(async ({ input, ctx }) => { const { templateId, fields } = input; - try { - return await setFieldsForTemplate({ - userId: ctx.user.id, - templateId, - fields: fields.map((field) => ({ - id: field.nativeId, - signerEmail: field.signerEmail, - type: field.type, - pageNumber: field.pageNumber, - pageX: field.pageX, - pageY: field.pageY, - pageWidth: field.pageWidth, - pageHeight: field.pageHeight, - fieldMeta: field.fieldMeta, - })), - }); - } catch (err) { - console.error(err); - - throw err; - } + return await setFieldsForTemplate({ + userId: ctx.user.id, + templateId, + fields: fields.map((field) => ({ + id: field.nativeId, + signerEmail: field.signerEmail, + type: field.type, + pageNumber: field.pageNumber, + pageX: field.pageX, + pageY: field.pageY, + pageWidth: field.pageWidth, + pageHeight: field.pageHeight, + fieldMeta: field.fieldMeta, + })), + }); }), signFieldWithToken: procedure .input(ZSignFieldWithTokenMutationSchema) .mutation(async ({ input, ctx }) => { - try { - const { token, fieldId, value, isBase64, authOptions } = input; + const { token, fieldId, value, isBase64, authOptions } = input; - return await signFieldWithToken({ - token, - fieldId, - value, - isBase64, - userId: ctx.user?.id, - authOptions, - requestMetadata: extractNextApiRequestMetadata(ctx.req), - }); - } catch (err) { - console.error(err); - - throw err; - } + return await signFieldWithToken({ + token, + fieldId, + value, + isBase64, + userId: ctx.user?.id, + authOptions, + requestMetadata: extractNextApiRequestMetadata(ctx.req), + }); }), removeSignedFieldWithToken: procedure .input(ZRemovedSignedFieldWithTokenMutationSchema) .mutation(async ({ input, ctx }) => { - try { - const { token, fieldId } = input; + const { token, fieldId } = input; - return await removeSignedFieldWithToken({ - token, - fieldId, - requestMetadata: extractNextApiRequestMetadata(ctx.req), - }); - } catch (err) { - console.error(err); - - throw new TRPCError({ - code: 'BAD_REQUEST', - message: 'We were unable to remove the signature for this field. Please try again later.', - }); - } + return await removeSignedFieldWithToken({ + token, + fieldId, + requestMetadata: extractNextApiRequestMetadata(ctx.req), + }); }), getField: authenticatedProcedure.input(ZGetFieldQuerySchema).query(async ({ input, ctx }) => { - try { - const { fieldId, teamId } = input; + const { fieldId, teamId } = input; - return await getFieldById({ - userId: ctx.user.id, - teamId, - fieldId, - }); - } catch (err) { - console.error(err); - - throw new TRPCError({ - code: 'BAD_REQUEST', - message: 'We were unable to find this field. Please try again.', - }); - } + return await getFieldById({ + userId: ctx.user.id, + teamId, + fieldId, + }); }), // This doesn't appear to be used anywhere, and it doesn't seem to support updating template fields diff --git a/packages/trpc/server/profile-router/router.ts b/packages/trpc/server/profile-router/router.ts index 7b6ec34cf..2ee779e65 100644 --- a/packages/trpc/server/profile-router/router.ts +++ b/packages/trpc/server/profile-router/router.ts @@ -1,5 +1,3 @@ -import { TRPCError } from '@trpc/server'; - import { IS_BILLING_ENABLED } from '@documenso/lib/constants/app'; import { AppError, AppErrorCode } from '@documenso/lib/errors/app-error'; import { jobsClient } from '@documenso/lib/jobs/client'; @@ -33,246 +31,122 @@ export const profileRouter = router({ findUserSecurityAuditLogs: authenticatedProcedure .input(ZFindUserSecurityAuditLogsSchema) .query(async ({ input, ctx }) => { - try { - return await findUserSecurityAuditLogs({ - userId: ctx.user.id, - ...input, - }); - } catch (err) { - console.error(err); - - throw new TRPCError({ - code: 'BAD_REQUEST', - message: 'We were unable to find user security audit logs. Please try again.', - }); - } + return await findUserSecurityAuditLogs({ + userId: ctx.user.id, + ...input, + }); }), getUser: adminProcedure.input(ZRetrieveUserByIdQuerySchema).query(async ({ input }) => { - try { - const { id } = input; + const { id } = input; - return await getUserById({ id }); - } catch (err) { - console.error(err); - - throw new TRPCError({ - code: 'BAD_REQUEST', - message: 'We were unable to retrieve the specified account. Please try again.', - }); - } + return await getUserById({ id }); }), updateProfile: authenticatedProcedure .input(ZUpdateProfileMutationSchema) .mutation(async ({ input, ctx }) => { - try { - const { name, signature } = input; + const { name, signature } = input; - return await updateProfile({ - userId: ctx.user.id, - name, - signature, - requestMetadata: extractNextApiRequestMetadata(ctx.req), - }); - } catch (err) { - console.error(err); - - throw new TRPCError({ - code: 'BAD_REQUEST', - message: - 'We were unable to update your profile. Please review the information you provided and try again.', - }); - } + return await updateProfile({ + userId: ctx.user.id, + name, + signature, + requestMetadata: extractNextApiRequestMetadata(ctx.req), + }); }), updatePublicProfile: authenticatedProcedure .input(ZUpdatePublicProfileMutationSchema) .mutation(async ({ input, ctx }) => { - try { - const { url, bio, enabled } = input; + const { url, bio, enabled } = input; - if (IS_BILLING_ENABLED() && url !== undefined && url.length < 6) { - const subscriptions = await getSubscriptionsByUserId({ - userId: ctx.user.id, - }).then((subscriptions) => - subscriptions.filter((s) => s.status === SubscriptionStatus.ACTIVE), - ); - - if (subscriptions.length === 0) { - throw new AppError(AppErrorCode.PREMIUM_PROFILE_URL, { - message: 'Only subscribers can have a username shorter than 6 characters', - }); - } - } - - const user = await updatePublicProfile({ + if (IS_BILLING_ENABLED() && url !== undefined && url.length < 6) { + const subscriptions = await getSubscriptionsByUserId({ userId: ctx.user.id, - data: { - url, - bio, - enabled, - }, - }); + }).then((subscriptions) => + subscriptions.filter((s) => s.status === SubscriptionStatus.ACTIVE), + ); - return { success: true, url: user.url }; - } catch (err) { - console.error(err); - - const error = AppError.parseError(err); - - if (error.code !== AppErrorCode.UNKNOWN_ERROR) { - throw error; + if (subscriptions.length === 0) { + throw new AppError(AppErrorCode.PREMIUM_PROFILE_URL, { + message: 'Only subscribers can have a username shorter than 6 characters', + }); } - - throw new TRPCError({ - code: 'BAD_REQUEST', - message: - 'We were unable to update your public profile. Please review the information you provided and try again.', - }); } + + const user = await updatePublicProfile({ + userId: ctx.user.id, + data: { + url, + bio, + enabled, + }, + }); + + return { success: true, url: user.url }; }), updatePassword: authenticatedProcedure .input(ZUpdatePasswordMutationSchema) .mutation(async ({ input, ctx }) => { - try { - const { password, currentPassword } = input; + const { password, currentPassword } = input; - return await updatePassword({ - userId: ctx.user.id, - password, - currentPassword, - requestMetadata: extractNextApiRequestMetadata(ctx.req), - }); - } catch (err) { - console.error(err); - - let message = - 'We were unable to update your profile. Please review the information you provided and try again.'; - - if (err instanceof Error) { - message = err.message; - } - - throw new TRPCError({ - code: 'BAD_REQUEST', - message, - }); - } + return await updatePassword({ + userId: ctx.user.id, + password, + currentPassword, + requestMetadata: extractNextApiRequestMetadata(ctx.req), + }); }), forgotPassword: procedure.input(ZForgotPasswordFormSchema).mutation(async ({ input }) => { - try { - const { email } = input; + const { email } = input; - return await forgotPassword({ - email, - }); - } catch (err) { - console.error(err); - } + return await forgotPassword({ + email, + }); }), resetPassword: procedure.input(ZResetPasswordFormSchema).mutation(async ({ input, ctx }) => { - try { - const { password, token } = input; + const { password, token } = input; - return await resetPassword({ - token, - password, - requestMetadata: extractNextApiRequestMetadata(ctx.req), - }); - } catch (err) { - console.error(err); - - let message = 'We were unable to reset your password. Please try again.'; - - if (err instanceof Error) { - message = err.message; - } - - throw new TRPCError({ - code: 'BAD_REQUEST', - message, - }); - } + return await resetPassword({ + token, + password, + requestMetadata: extractNextApiRequestMetadata(ctx.req), + }); }), sendConfirmationEmail: procedure .input(ZConfirmEmailMutationSchema) .mutation(async ({ input }) => { - try { - const { email } = input; + const { email } = input; - await jobsClient.triggerJob({ - name: 'send.signup.confirmation.email', - payload: { - email, - }, - }); - } catch (err) { - console.error(err); - - let message = 'We were unable to send a confirmation email. Please try again.'; - - if (err instanceof Error) { - message = err.message; - } - - throw new TRPCError({ - code: 'BAD_REQUEST', - message, - }); - } + await jobsClient.triggerJob({ + name: 'send.signup.confirmation.email', + payload: { + email, + }, + }); }), deleteAccount: authenticatedProcedure.mutation(async ({ ctx }) => { - try { - return await deleteUser({ - id: ctx.user.id, - }); - } catch (err) { - console.error(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, - }); - } + return await deleteUser({ + id: ctx.user.id, + }); }), setProfileImage: authenticatedProcedure .input(ZSetProfileImageMutationSchema) .mutation(async ({ input, ctx }) => { - try { - const { bytes, teamId } = input; + const { bytes, teamId } = input; - return await setAvatarImage({ - userId: ctx.user.id, - teamId, - bytes, - requestMetadata: extractNextApiRequestMetadata(ctx.req), - }); - } catch (err) { - console.error(err); - - let message = 'We were unable to update your profile image. Please try again.'; - - if (err instanceof Error) { - message = err.message; - } - - throw new TRPCError({ - code: 'BAD_REQUEST', - message, - }); - } + return await setAvatarImage({ + userId: ctx.user.id, + teamId, + bytes, + requestMetadata: extractNextApiRequestMetadata(ctx.req), + }); }), }); diff --git a/packages/trpc/server/recipient-router/router.ts b/packages/trpc/server/recipient-router/router.ts index f106ff553..cfb4a84c7 100644 --- a/packages/trpc/server/recipient-router/router.ts +++ b/packages/trpc/server/recipient-router/router.ts @@ -1,5 +1,3 @@ -import { TRPCError } from '@trpc/server'; - import { completeDocumentWithToken } from '@documenso/lib/server-only/document/complete-document-with-token'; import { rejectDocumentWithToken } from '@documenso/lib/server-only/document/reject-document-with-token'; import { setRecipientsForDocument } from '@documenso/lib/server-only/recipient/set-recipients-for-document'; @@ -18,104 +16,68 @@ export const recipientRouter = router({ addSigners: authenticatedProcedure .input(ZAddSignersMutationSchema) .mutation(async ({ input, ctx }) => { - try { - const { documentId, teamId, 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, - name: signer.name, - role: signer.role, - signingOrder: signer.signingOrder, - actionAuth: signer.actionAuth, - })), - requestMetadata: extractNextApiRequestMetadata(ctx.req), - }); - } catch (err) { - console.error(err); - - throw new TRPCError({ - code: 'BAD_REQUEST', - message: 'We were unable to set this field. Please try again later.', - }); - } + return await setRecipientsForDocument({ + userId: ctx.user.id, + documentId, + teamId, + recipients: signers.map((signer) => ({ + id: signer.nativeId, + email: signer.email, + name: signer.name, + role: signer.role, + signingOrder: signer.signingOrder, + actionAuth: signer.actionAuth, + })), + requestMetadata: extractNextApiRequestMetadata(ctx.req), + }); }), addTemplateSigners: authenticatedProcedure .input(ZAddTemplateSignersMutationSchema) .mutation(async ({ input, ctx }) => { - try { - const { templateId, signers, teamId } = input; + const { templateId, signers, teamId } = input; - return await setRecipientsForTemplate({ - userId: ctx.user.id, - teamId, - templateId, - recipients: signers.map((signer) => ({ - id: signer.nativeId, - email: signer.email, - name: signer.name, - role: signer.role, - signingOrder: signer.signingOrder, - actionAuth: signer.actionAuth, - })), - }); - } catch (err) { - console.error(err); - - throw new TRPCError({ - code: 'BAD_REQUEST', - message: 'We were unable to set this field. Please try again later.', - }); - } + return await setRecipientsForTemplate({ + userId: ctx.user.id, + teamId, + templateId, + recipients: signers.map((signer) => ({ + id: signer.nativeId, + email: signer.email, + name: signer.name, + role: signer.role, + signingOrder: signer.signingOrder, + actionAuth: signer.actionAuth, + })), + }); }), completeDocumentWithToken: procedure .input(ZCompleteDocumentWithTokenMutationSchema) .mutation(async ({ input, ctx }) => { - try { - const { token, documentId, authOptions } = input; + const { token, documentId, authOptions } = input; - return await completeDocumentWithToken({ - token, - documentId, - authOptions, - userId: ctx.user?.id, - requestMetadata: extractNextApiRequestMetadata(ctx.req), - }); - } catch (err) { - console.error(err); - - throw new TRPCError({ - code: 'BAD_REQUEST', - message: 'We were unable to sign this field. Please try again later.', - }); - } + return await completeDocumentWithToken({ + token, + documentId, + authOptions, + userId: ctx.user?.id, + requestMetadata: extractNextApiRequestMetadata(ctx.req), + }); }), rejectDocumentWithToken: procedure .input(ZRejectDocumentWithTokenMutationSchema) .mutation(async ({ input, ctx }) => { - try { - const { token, documentId, reason } = input; + const { token, documentId, reason } = input; - return await rejectDocumentWithToken({ - token, - documentId, - reason, - requestMetadata: extractNextApiRequestMetadata(ctx.req), - }); - } catch (err) { - console.error(err); - - throw new TRPCError({ - code: 'BAD_REQUEST', - message: 'We were unable to handle this request. Please try again later.', - }); - } + return await rejectDocumentWithToken({ + token, + documentId, + reason, + requestMetadata: extractNextApiRequestMetadata(ctx.req), + }); }), }); diff --git a/packages/trpc/server/router.ts b/packages/trpc/server/router.ts index 16f79f712..f0781d880 100644 --- a/packages/trpc/server/router.ts +++ b/packages/trpc/server/router.ts @@ -1,7 +1,6 @@ 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'; import { fieldRouter } from './field-router/router'; import { profileRouter } from './profile-router/router'; @@ -16,7 +15,6 @@ import { webhookRouter } from './webhook-router/router'; export const appRouter = router({ auth: authRouter, - crypto: cryptoRouter, profile: profileRouter, document: documentRouter, field: fieldRouter, diff --git a/packages/trpc/server/share-link-router/router.ts b/packages/trpc/server/share-link-router/router.ts index 00dd2a8ef..517e207e8 100644 --- a/packages/trpc/server/share-link-router/router.ts +++ b/packages/trpc/server/share-link-router/router.ts @@ -1,5 +1,3 @@ -import { TRPCError } from '@trpc/server'; - import { createOrGetShareLink } from '@documenso/lib/server-only/share/create-or-get-share-link'; import { procedure, router } from '../trpc'; @@ -9,27 +7,18 @@ export const shareLinkRouter = router({ createOrGetShareLink: procedure .input(ZCreateOrGetShareLinkMutationSchema) .mutation(async ({ ctx, input }) => { - try { - const { documentId, token } = input; + const { documentId, token } = input; - if (token) { - return await createOrGetShareLink({ documentId, token }); - } - - if (!ctx.user?.id) { - throw new Error( - 'You must either provide a token or be logged in to create a sharing link.', - ); - } - - return await createOrGetShareLink({ documentId, userId: ctx.user.id }); - } catch (err) { - console.error(err); - - throw new TRPCError({ - code: 'BAD_REQUEST', - message: 'We were unable to create a sharing link.', - }); + if (token) { + return await createOrGetShareLink({ documentId, token }); } + + if (!ctx.user?.id) { + throw new Error( + 'You must either provide a token or be logged in to create a sharing link.', + ); + } + + return await createOrGetShareLink({ documentId, userId: ctx.user.id }); }), }); diff --git a/packages/trpc/server/singleplayer-router/router.ts b/packages/trpc/server/singleplayer-router/router.ts index 5cf62cb53..6363ccfa6 100644 --- a/packages/trpc/server/singleplayer-router/router.ts +++ b/packages/trpc/server/singleplayer-router/router.ts @@ -30,159 +30,153 @@ export const singleplayerRouter = router({ createSinglePlayerDocument: procedure .input(ZCreateSinglePlayerDocumentMutationSchema) .mutation(async ({ input }) => { - try { - const { signer, fields, documentData, documentName, fieldMeta } = input; + const { signer, fields, documentData, documentName, fieldMeta } = input; - const document = await getFile({ - data: documentData.data, - type: documentData.type, + const document = await getFile({ + data: documentData.data, + type: documentData.type, + }); + + const doc = await PDFDocument.load(document); + + const createdAt = new Date(); + + const isBase64 = signer.signature.startsWith('data:image/png;base64,'); + const signatureImageAsBase64 = isBase64 ? signer.signature : null; + const typedSignature = !isBase64 ? signer.signature : null; + + // Update the document with the fields inserted. + for (const field of fields) { + const isSignatureField = field.type === FieldType.SIGNATURE; + + await insertFieldInPDF(doc, { + ...mapField(field, signer), + Signature: isSignatureField + ? { + created: createdAt, + signatureImageAsBase64, + typedSignature, + // Dummy data. + id: -1, + recipientId: -1, + fieldId: -1, + } + : null, + // Dummy data. + id: -1, + secondaryId: '-1', + documentId: -1, + templateId: null, + recipientId: -1, + fieldMeta: fieldMeta || null, }); + } - const doc = await PDFDocument.load(document); + const unsignedPdfBytes = await doc.save(); - const createdAt = new Date(); + const signedPdfBuffer = await signPdf({ pdf: Buffer.from(unsignedPdfBytes) }); - const isBase64 = signer.signature.startsWith('data:image/png;base64,'); - const signatureImageAsBase64 = isBase64 ? signer.signature : null; - const typedSignature = !isBase64 ? signer.signature : null; + const { token } = await prisma.$transaction( + async (tx) => { + const token = alphaid(); - // Update the document with the fields inserted. - for (const field of fields) { - const isSignatureField = field.type === FieldType.SIGNATURE; - - await insertFieldInPDF(doc, { - ...mapField(field, signer), - Signature: isSignatureField - ? { - created: createdAt, - signatureImageAsBase64, - typedSignature, - // Dummy data. - id: -1, - recipientId: -1, - fieldId: -1, - } - : null, - // Dummy data. - id: -1, - secondaryId: '-1', - documentId: -1, - templateId: null, - recipientId: -1, - fieldMeta: fieldMeta || null, + // Fetch service user who will be the owner of the document. + const serviceUser = await tx.user.findFirstOrThrow({ + where: { + email: SERVICE_USER_EMAIL, + }, }); - } - const unsignedPdfBytes = await doc.save(); + const { id: documentDataId } = await putPdfFile({ + name: `${documentName}.pdf`, + type: 'application/pdf', + arrayBuffer: async () => Promise.resolve(signedPdfBuffer), + }); - const signedPdfBuffer = await signPdf({ pdf: Buffer.from(unsignedPdfBytes) }); + // Create document. + const document = await tx.document.create({ + data: { + source: DocumentSource.DOCUMENT, + title: documentName, + status: DocumentStatus.COMPLETED, + documentDataId, + userId: serviceUser.id, + createdAt, + }, + }); - const { token } = await prisma.$transaction( - async (tx) => { - const token = alphaid(); + // Create recipient. + const recipient = await tx.recipient.create({ + data: { + documentId: document.id, + name: signer.name, + email: signer.email, + token, + signedAt: createdAt, + readStatus: ReadStatus.OPENED, + signingStatus: SigningStatus.SIGNED, + sendStatus: SendStatus.SENT, + }, + }); - // Fetch service user who will be the owner of the document. - const serviceUser = await tx.user.findFirstOrThrow({ - where: { - email: SERVICE_USER_EMAIL, - }, - }); + // Create fields and signatures. + await Promise.all( + fields.map(async (field) => { + const insertedField = await tx.field.create({ + data: { + documentId: document.id, + recipientId: recipient.id, + ...mapField(field, signer), + }, + }); - const { id: documentDataId } = await putPdfFile({ - name: `${documentName}.pdf`, - type: 'application/pdf', - arrayBuffer: async () => Promise.resolve(signedPdfBuffer), - }); - - // Create document. - const document = await tx.document.create({ - data: { - source: DocumentSource.DOCUMENT, - title: documentName, - status: DocumentStatus.COMPLETED, - documentDataId, - userId: serviceUser.id, - createdAt, - }, - }); - - // Create recipient. - const recipient = await tx.recipient.create({ - data: { - documentId: document.id, - name: signer.name, - email: signer.email, - token, - signedAt: createdAt, - readStatus: ReadStatus.OPENED, - signingStatus: SigningStatus.SIGNED, - sendStatus: SendStatus.SENT, - }, - }); - - // Create fields and signatures. - await Promise.all( - fields.map(async (field) => { - const insertedField = await tx.field.create({ + if (field.type === FieldType.SIGNATURE || field.type === FieldType.FREE_SIGNATURE) { + await tx.signature.create({ data: { - documentId: document.id, + fieldId: insertedField.id, + signatureImageAsBase64, + typedSignature, recipientId: recipient.id, - ...mapField(field, signer), }, }); + } + }), + ); - if (field.type === FieldType.SIGNATURE || field.type === FieldType.FREE_SIGNATURE) { - await tx.signature.create({ - data: { - fieldId: insertedField.id, - signatureImageAsBase64, - typedSignature, - recipientId: recipient.id, - }, - }); - } - }), - ); + return { document, token }; + }, + { + maxWait: 5000, + timeout: 30000, + }, + ); - return { document, token }; - }, - { - maxWait: 5000, - timeout: 30000, - }, - ); + const template = createElement(DocumentSelfSignedEmailTemplate, { + documentName: documentName, + assetBaseUrl: NEXT_PUBLIC_WEBAPP_URL() || 'http://localhost:3000', + }); - const template = createElement(DocumentSelfSignedEmailTemplate, { - documentName: documentName, - assetBaseUrl: NEXT_PUBLIC_WEBAPP_URL() || 'http://localhost:3000', - }); + const [html, text] = await Promise.all([ + renderEmailWithI18N(template), + renderEmailWithI18N(template, { plainText: true }), + ]); - const [html, text] = await Promise.all([ - renderEmailWithI18N(template), - renderEmailWithI18N(template, { plainText: true }), - ]); + // Send email to signer. + await mailer.sendMail({ + to: { + address: signer.email, + name: signer.name, + }, + from: { + name: FROM_NAME, + address: FROM_ADDRESS, + }, + subject: 'Document signed', + html, + text, + attachments: [{ content: signedPdfBuffer, filename: documentName }], + }); - // Send email to signer. - await mailer.sendMail({ - to: { - address: signer.email, - name: signer.name, - }, - from: { - name: FROM_NAME, - address: FROM_ADDRESS, - }, - subject: 'Document signed', - html, - text, - attachments: [{ content: signedPdfBuffer, filename: documentName }], - }); - - return token; - } catch (err) { - console.error(err); - - throw err; - } + return token; }), }); diff --git a/packages/trpc/server/two-factor-authentication-router/router.ts b/packages/trpc/server/two-factor-authentication-router/router.ts index e8b60295c..5e4daab95 100644 --- a/packages/trpc/server/two-factor-authentication-router/router.ts +++ b/packages/trpc/server/two-factor-authentication-router/router.ts @@ -1,6 +1,3 @@ -import { TRPCError } from '@trpc/server'; - -import { AppError } from '@documenso/lib/errors/app-error'; import { disableTwoFactorAuthentication } from '@documenso/lib/server-only/2fa/disable-2fa'; import { enableTwoFactorAuthentication } from '@documenso/lib/server-only/2fa/enable-2fa'; import { setupTwoFactorAuthentication } from '@documenso/lib/server-only/2fa/setup-2fa'; @@ -16,89 +13,44 @@ import { export const twoFactorAuthenticationRouter = router({ setup: authenticatedProcedure.mutation(async ({ ctx }) => { - try { - return await setupTwoFactorAuthentication({ - user: ctx.user, - }); - } catch (err) { - console.error(err); - - throw new TRPCError({ - code: 'BAD_REQUEST', - message: 'We were unable to setup two-factor authentication. Please try again later.', - }); - } + return await setupTwoFactorAuthentication({ + user: ctx.user, + }); }), enable: authenticatedProcedure .input(ZEnableTwoFactorAuthenticationMutationSchema) .mutation(async ({ ctx, input }) => { - try { - const user = ctx.user; + const user = ctx.user; - const { code } = input; + const { code } = input; - return await enableTwoFactorAuthentication({ - user, - code, - requestMetadata: extractNextApiRequestMetadata(ctx.req), - }); - } catch (err) { - const error = AppError.parseError(err); - - if (error.code !== 'INCORRECT_TWO_FACTOR_CODE') { - console.error(err); - } - - throw new TRPCError({ - code: 'BAD_REQUEST', - message: 'We were unable to enable two-factor authentication. Please try again later.', - }); - } + return await enableTwoFactorAuthentication({ + user, + code, + requestMetadata: extractNextApiRequestMetadata(ctx.req), + }); }), disable: authenticatedProcedure .input(ZDisableTwoFactorAuthenticationMutationSchema) .mutation(async ({ ctx, input }) => { - try { - const user = ctx.user; + const user = ctx.user; - return await disableTwoFactorAuthentication({ - user, - totpCode: input.totpCode, - backupCode: input.backupCode, - requestMetadata: extractNextApiRequestMetadata(ctx.req), - }); - } catch (err) { - const error = AppError.parseError(err); - - if (error.code !== 'INCORRECT_TWO_FACTOR_CODE') { - console.error(err); - } - - throw new TRPCError({ - code: 'BAD_REQUEST', - message: 'We were unable to disable two-factor authentication. Please try again later.', - }); - } + return await disableTwoFactorAuthentication({ + user, + totpCode: input.totpCode, + backupCode: input.backupCode, + requestMetadata: extractNextApiRequestMetadata(ctx.req), + }); }), viewRecoveryCodes: authenticatedProcedure .input(ZViewRecoveryCodesMutationSchema) .mutation(async ({ ctx, input }) => { - try { - return await viewBackupCodes({ - user: ctx.user, - token: input.token, - }); - } catch (err) { - const error = AppError.parseError(err); - - if (error.code !== 'INCORRECT_TWO_FACTOR_CODE') { - console.error(err); - } - - throw error; - } + return await viewBackupCodes({ + user: ctx.user, + token: input.token, + }); }), }); diff --git a/packages/trpc/server/webhook-router/router.ts b/packages/trpc/server/webhook-router/router.ts index d1479457b..730b23885 100644 --- a/packages/trpc/server/webhook-router/router.ts +++ b/packages/trpc/server/webhook-router/router.ts @@ -1,5 +1,3 @@ -import { TRPCError } from '@trpc/server'; - import { createWebhook } from '@documenso/lib/server-only/webhooks/create-webhook'; import { deleteWebhookById } from '@documenso/lib/server-only/webhooks/delete-webhook-by-id'; import { editWebhook } from '@documenso/lib/server-only/webhooks/edit-webhook'; @@ -18,16 +16,7 @@ import { export const webhookRouter = router({ getWebhooks: authenticatedProcedure.query(async ({ ctx }) => { - try { - return await getWebhooksByUserId(ctx.user.id); - } catch (err) { - console.error(err); - - throw new TRPCError({ - code: 'BAD_REQUEST', - message: 'We were unable to fetch your webhooks. Please try again later.', - }); - } + return await getWebhooksByUserId(ctx.user.id); }), getTeamWebhooks: authenticatedProcedure @@ -35,37 +24,19 @@ export const webhookRouter = router({ .query(async ({ ctx, input }) => { const { teamId } = input; - try { - return await getWebhooksByTeamId(teamId, ctx.user.id); - } catch (err) { - console.error(err); - - throw new TRPCError({ - code: 'BAD_REQUEST', - message: 'We were unable to fetch your webhooks. Please try again later.', - }); - } + return await getWebhooksByTeamId(teamId, ctx.user.id); }), getWebhookById: authenticatedProcedure .input(ZGetWebhookByIdQuerySchema) .query(async ({ input, ctx }) => { - try { - const { id, teamId } = input; + const { id, teamId } = input; - return await getWebhookById({ - id, - userId: ctx.user.id, - teamId, - }); - } catch (err) { - console.error(err); - - throw new TRPCError({ - code: 'BAD_REQUEST', - message: 'We were unable to fetch your webhook. Please try again later.', - }); - } + return await getWebhookById({ + id, + userId: ctx.user.id, + teamId, + }); }), createWebhook: authenticatedProcedure @@ -73,65 +44,38 @@ export const webhookRouter = router({ .mutation(async ({ input, ctx }) => { const { enabled, eventTriggers, secret, webhookUrl, teamId } = input; - try { - return await createWebhook({ - enabled, - secret, - webhookUrl, - eventTriggers, - teamId, - userId: ctx.user.id, - }); - } catch (err) { - console.error(err); - - throw new TRPCError({ - code: 'BAD_REQUEST', - message: 'We were unable to create this webhook. Please try again later.', - }); - } + return await createWebhook({ + enabled, + secret, + webhookUrl, + eventTriggers, + teamId, + userId: ctx.user.id, + }); }), deleteWebhook: authenticatedProcedure .input(ZDeleteWebhookMutationSchema) .mutation(async ({ input, ctx }) => { - try { - const { id, teamId } = input; + const { id, teamId } = input; - return await deleteWebhookById({ - id, - teamId, - userId: ctx.user.id, - }); - } catch (err) { - console.error(err); - - throw new TRPCError({ - code: 'BAD_REQUEST', - message: 'We were unable to create this webhook. Please try again later.', - }); - } + return await deleteWebhookById({ + id, + teamId, + userId: ctx.user.id, + }); }), editWebhook: authenticatedProcedure .input(ZEditWebhookMutationSchema) .mutation(async ({ input, ctx }) => { - try { - const { id, teamId, ...data } = input; + const { id, teamId, ...data } = input; - return await editWebhook({ - id, - data, - userId: ctx.user.id, - teamId, - }); - } catch (err) { - console.error(err); - - throw new TRPCError({ - code: 'BAD_REQUEST', - message: 'We were unable to create this webhook. Please try again later.', - }); - } + return await editWebhook({ + id, + data, + userId: ctx.user.id, + teamId, + }); }), });