fix: refactor trpc errors (#1511)
This commit is contained in:
@@ -7,7 +7,6 @@ import { useLingui } from '@lingui/react';
|
|||||||
import { signOut } from 'next-auth/react';
|
import { signOut } from 'next-auth/react';
|
||||||
|
|
||||||
import type { User } from '@documenso/prisma/client';
|
import type { User } from '@documenso/prisma/client';
|
||||||
import { TRPCClientError } from '@documenso/trpc/client';
|
|
||||||
import { trpc } from '@documenso/trpc/react';
|
import { trpc } from '@documenso/trpc/react';
|
||||||
import { Alert, AlertDescription, AlertTitle } from '@documenso/ui/primitives/alert';
|
import { Alert, AlertDescription, AlertTitle } from '@documenso/ui/primitives/alert';
|
||||||
import { Button } from '@documenso/ui/primitives/button';
|
import { Button } from '@documenso/ui/primitives/button';
|
||||||
@@ -52,30 +51,20 @@ export const DeleteAccountDialog = ({ className, user }: DeleteAccountDialogProp
|
|||||||
|
|
||||||
return await signOut({ callbackUrl: '/' });
|
return await signOut({ callbackUrl: '/' });
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
if (err instanceof TRPCClientError && err.data?.code === 'BAD_REQUEST') {
|
toast({
|
||||||
toast({
|
title: _(msg`An unknown error occurred`),
|
||||||
title: _(msg`An error occurred`),
|
variant: 'destructive',
|
||||||
description: err.message,
|
description: _(
|
||||||
variant: 'destructive',
|
msg`We encountered an unknown error while attempting to delete your account. Please try again later.`,
|
||||||
});
|
),
|
||||||
} 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.`,
|
|
||||||
),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={className}>
|
<div className={className}>
|
||||||
<Alert
|
<Alert
|
||||||
className="flex flex-col items-center justify-between gap-4 p-6 md:flex-row "
|
className="flex flex-col items-center justify-between gap-4 p-6 md:flex-row"
|
||||||
variant="neutral"
|
variant="neutral"
|
||||||
>
|
>
|
||||||
<div>
|
<div>
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ import { useForm } from 'react-hook-form';
|
|||||||
import { z } from 'zod';
|
import { z } from 'zod';
|
||||||
|
|
||||||
import { useAnalytics } from '@documenso/lib/client-only/hooks/use-analytics';
|
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 { trpc } from '@documenso/trpc/react';
|
||||||
import { ZPasswordSchema } from '@documenso/trpc/server/auth-router/schema';
|
import { ZPasswordSchema } from '@documenso/trpc/server/auth-router/schema';
|
||||||
import { Button } from '@documenso/ui/primitives/button';
|
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 { PasswordInput } from '@documenso/ui/primitives/password-input';
|
||||||
import { useToast } from '@documenso/ui/primitives/use-toast';
|
import { useToast } from '@documenso/ui/primitives/use-toast';
|
||||||
|
|
||||||
|
import { signupErrorMessages } from '~/components/forms/v2/signup';
|
||||||
|
|
||||||
export type ClaimAccountProps = {
|
export type ClaimAccountProps = {
|
||||||
defaultName: string;
|
defaultName: string;
|
||||||
defaultEmail: string;
|
defaultEmail: string;
|
||||||
@@ -33,7 +35,10 @@ export type ClaimAccountProps = {
|
|||||||
|
|
||||||
export const ZClaimAccountFormSchema = z
|
export const ZClaimAccountFormSchema = z
|
||||||
.object({
|
.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),
|
email: z.string().email().min(1),
|
||||||
password: ZPasswordSchema,
|
password: ZPasswordSchema,
|
||||||
})
|
})
|
||||||
@@ -43,7 +48,7 @@ export const ZClaimAccountFormSchema = z
|
|||||||
return !password.includes(name) && !password.includes(email.split('@')[0]);
|
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'],
|
path: ['password'],
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
@@ -86,22 +91,16 @@ export const ClaimAccount = ({ defaultName, defaultEmail }: ClaimAccountProps) =
|
|||||||
email,
|
email,
|
||||||
timestamp: new Date().toISOString(),
|
timestamp: new Date().toISOString(),
|
||||||
});
|
});
|
||||||
} catch (error) {
|
} catch (err) {
|
||||||
if (error instanceof TRPCClientError && error.data?.code === 'BAD_REQUEST') {
|
const error = AppError.parseError(err);
|
||||||
toast({
|
|
||||||
title: _(msg`An error occurred`),
|
const errorMessage = signupErrorMessages[error.code] ?? signupErrorMessages.INVALID_REQUEST;
|
||||||
description: error.message,
|
|
||||||
variant: 'destructive',
|
toast({
|
||||||
});
|
title: _(msg`An error occurred`),
|
||||||
} else {
|
description: _(errorMessage),
|
||||||
toast({
|
variant: 'destructive',
|
||||||
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',
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -13,10 +13,10 @@ import { match } from 'ts-pattern';
|
|||||||
import { z } from 'zod';
|
import { z } from 'zod';
|
||||||
|
|
||||||
import { NEXT_PUBLIC_WEBAPP_URL } from '@documenso/lib/constants/app';
|
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 { base64 } from '@documenso/lib/universal/base64';
|
||||||
import { extractInitials } from '@documenso/lib/utils/recipient-formatter';
|
import { extractInitials } from '@documenso/lib/utils/recipient-formatter';
|
||||||
import type { Team, User } from '@documenso/prisma/client';
|
import type { Team, User } from '@documenso/prisma/client';
|
||||||
import { TRPCClientError } from '@documenso/trpc/client';
|
|
||||||
import { trpc } from '@documenso/trpc/react';
|
import { trpc } from '@documenso/trpc/react';
|
||||||
import { cn } from '@documenso/ui/lib/utils';
|
import { cn } from '@documenso/ui/lib/utils';
|
||||||
import { Avatar, AvatarFallback, AvatarImage } from '@documenso/ui/primitives/avatar';
|
import { Avatar, AvatarFallback, AvatarImage } from '@documenso/ui/primitives/avatar';
|
||||||
@@ -111,21 +111,18 @@ export const AvatarImageForm = ({ className, user, team }: AvatarImageFormProps)
|
|||||||
|
|
||||||
router.refresh();
|
router.refresh();
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
if (err instanceof TRPCClientError && err.data?.code === 'BAD_REQUEST') {
|
const error = AppError.parseError(err);
|
||||||
toast({
|
|
||||||
title: _(msg`An error occurred`),
|
const errorMessage = match(error.code).otherwise(
|
||||||
description: err.message,
|
() =>
|
||||||
variant: 'destructive',
|
msg`We encountered an unknown error while attempting to update your password. Please try again later.`,
|
||||||
});
|
);
|
||||||
} else {
|
|
||||||
toast({
|
toast({
|
||||||
title: _(msg`An unknown error occurred`),
|
title: _(msg`An error occurred`),
|
||||||
description: _(
|
description: _(errorMessage),
|
||||||
msg`We encountered an unknown error while attempting to update the avatar. Please try again later.`,
|
variant: 'destructive',
|
||||||
),
|
});
|
||||||
variant: 'destructive',
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -4,10 +4,11 @@ import { zodResolver } from '@hookform/resolvers/zod';
|
|||||||
import { Trans, msg } from '@lingui/macro';
|
import { Trans, msg } from '@lingui/macro';
|
||||||
import { useLingui } from '@lingui/react';
|
import { useLingui } from '@lingui/react';
|
||||||
import { useForm } from 'react-hook-form';
|
import { useForm } from 'react-hook-form';
|
||||||
|
import { match } from 'ts-pattern';
|
||||||
import { z } from 'zod';
|
import { z } from 'zod';
|
||||||
|
|
||||||
|
import { AppError } from '@documenso/lib/errors/app-error';
|
||||||
import type { User } from '@documenso/prisma/client';
|
import type { User } from '@documenso/prisma/client';
|
||||||
import { TRPCClientError } from '@documenso/trpc/client';
|
|
||||||
import { trpc } from '@documenso/trpc/react';
|
import { trpc } from '@documenso/trpc/react';
|
||||||
import { ZCurrentPasswordSchema, ZPasswordSchema } from '@documenso/trpc/server/auth-router/schema';
|
import { ZCurrentPasswordSchema, ZPasswordSchema } from '@documenso/trpc/server/auth-router/schema';
|
||||||
import { cn } from '@documenso/ui/lib/utils';
|
import { cn } from '@documenso/ui/lib/utils';
|
||||||
@@ -73,21 +74,25 @@ export const PasswordForm = ({ className }: PasswordFormProps) => {
|
|||||||
duration: 5000,
|
duration: 5000,
|
||||||
});
|
});
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
if (err instanceof TRPCClientError && err.data?.code === 'BAD_REQUEST') {
|
const error = AppError.parseError(err);
|
||||||
toast({
|
|
||||||
title: _(msg`An error occurred`),
|
const errorMessage = match(error.code)
|
||||||
description: err.message,
|
.with('NO_PASSWORD', () => msg`User has no password.`)
|
||||||
variant: 'destructive',
|
.with('INCORRECT_PASSWORD', () => msg`Current password is incorrect.`)
|
||||||
});
|
.with(
|
||||||
} else {
|
'SAME_PASSWORD',
|
||||||
toast({
|
() => msg`Your new password cannot be the same as your old password.`,
|
||||||
title: _(msg`An unknown error occurred`),
|
)
|
||||||
description: _(
|
.otherwise(
|
||||||
|
() =>
|
||||||
msg`We encountered an unknown error while attempting to update your password. Please try again later.`,
|
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',
|
||||||
|
});
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -6,9 +6,10 @@ import { zodResolver } from '@hookform/resolvers/zod';
|
|||||||
import { Trans, msg } from '@lingui/macro';
|
import { Trans, msg } from '@lingui/macro';
|
||||||
import { useLingui } from '@lingui/react';
|
import { useLingui } from '@lingui/react';
|
||||||
import { useForm } from 'react-hook-form';
|
import { useForm } from 'react-hook-form';
|
||||||
|
import { match } from 'ts-pattern';
|
||||||
import { z } from 'zod';
|
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 { trpc } from '@documenso/trpc/react';
|
||||||
import { ZPasswordSchema } from '@documenso/trpc/server/auth-router/schema';
|
import { ZPasswordSchema } from '@documenso/trpc/server/auth-router/schema';
|
||||||
import { cn } from '@documenso/ui/lib/utils';
|
import { cn } from '@documenso/ui/lib/utils';
|
||||||
@@ -76,21 +77,25 @@ export const ResetPasswordForm = ({ className, token }: ResetPasswordFormProps)
|
|||||||
|
|
||||||
router.push('/signin');
|
router.push('/signin');
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
if (err instanceof TRPCClientError && err.data?.code === 'BAD_REQUEST') {
|
const error = AppError.parseError(err);
|
||||||
toast({
|
|
||||||
title: _(msg`An error occurred`),
|
const errorMessage = match(error.code)
|
||||||
description: err.message,
|
.with(AppErrorCode.EXPIRED_CODE, () => msg`Token has expired. Please try again.`)
|
||||||
variant: 'destructive',
|
.with('INVALID_TOKEN', () => msg`Invalid token provided. Please try again.`)
|
||||||
});
|
.with(
|
||||||
} else {
|
'SAME_PASSWORD',
|
||||||
toast({
|
() => msg`Your new password cannot be the same as your old password.`,
|
||||||
title: _(msg`An unknown error occurred`),
|
)
|
||||||
description: _(
|
.otherwise(
|
||||||
|
() =>
|
||||||
msg`We encountered an unknown error while attempting to reset your password. Please try again later.`,
|
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',
|
||||||
|
});
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ import { FcGoogle } from 'react-icons/fc';
|
|||||||
import { z } from 'zod';
|
import { z } from 'zod';
|
||||||
|
|
||||||
import { useAnalytics } from '@documenso/lib/client-only/hooks/use-analytics';
|
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 { trpc } from '@documenso/trpc/react';
|
||||||
import { ZPasswordSchema } from '@documenso/trpc/server/auth-router/schema';
|
import { ZPasswordSchema } from '@documenso/trpc/server/auth-router/schema';
|
||||||
import { cn } from '@documenso/ui/lib/utils';
|
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 { SignaturePad } from '@documenso/ui/primitives/signature-pad';
|
||||||
import { useToast } from '@documenso/ui/primitives/use-toast';
|
import { useToast } from '@documenso/ui/primitives/use-toast';
|
||||||
|
|
||||||
|
import { signupErrorMessages } from './v2/signup';
|
||||||
|
|
||||||
const SIGN_UP_REDIRECT_PATH = '/documents';
|
const SIGN_UP_REDIRECT_PATH = '/documents';
|
||||||
|
|
||||||
export const ZSignUpFormSchema = z
|
export const ZSignUpFormSchema = z
|
||||||
@@ -102,21 +104,15 @@ export const SignUpForm = ({
|
|||||||
timestamp: new Date().toISOString(),
|
timestamp: new Date().toISOString(),
|
||||||
});
|
});
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
if (err instanceof TRPCClientError && err.data?.code === 'BAD_REQUEST') {
|
const error = AppError.parseError(err);
|
||||||
toast({
|
|
||||||
title: _(msg`An error occurred`),
|
const errorMessage = signupErrorMessages[error.code] ?? signupErrorMessages.INVALID_REQUEST;
|
||||||
description: err.message,
|
|
||||||
variant: 'destructive',
|
toast({
|
||||||
});
|
title: _(msg`An error occurred`),
|
||||||
} else {
|
description: _(errorMessage),
|
||||||
toast({
|
variant: 'destructive',
|
||||||
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',
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import Link from 'next/link';
|
|||||||
import { useRouter, useSearchParams } from 'next/navigation';
|
import { useRouter, useSearchParams } from 'next/navigation';
|
||||||
|
|
||||||
import { zodResolver } from '@hookform/resolvers/zod';
|
import { zodResolver } from '@hookform/resolvers/zod';
|
||||||
|
import type { MessageDescriptor } from '@lingui/core';
|
||||||
import { Trans, msg } from '@lingui/macro';
|
import { Trans, msg } from '@lingui/macro';
|
||||||
import { useLingui } from '@lingui/react';
|
import { useLingui } from '@lingui/react';
|
||||||
import { AnimatePresence, motion } from 'framer-motion';
|
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 { useAnalytics } from '@documenso/lib/client-only/hooks/use-analytics';
|
||||||
import { NEXT_PUBLIC_WEBAPP_URL } from '@documenso/lib/constants/app';
|
import { NEXT_PUBLIC_WEBAPP_URL } from '@documenso/lib/constants/app';
|
||||||
import { AppError, AppErrorCode } from '@documenso/lib/errors/app-error';
|
import { AppError, AppErrorCode } from '@documenso/lib/errors/app-error';
|
||||||
import { TRPCClientError } from '@documenso/trpc/client';
|
|
||||||
import { trpc } from '@documenso/trpc/react';
|
import { trpc } from '@documenso/trpc/react';
|
||||||
import { ZPasswordSchema } from '@documenso/trpc/server/auth-router/schema';
|
import { ZPasswordSchema } from '@documenso/trpc/server/auth-router/schema';
|
||||||
import { cn } from '@documenso/ui/lib/utils';
|
import { cn } from '@documenso/ui/lib/utils';
|
||||||
@@ -47,17 +47,20 @@ type SignUpStep = 'BASIC_DETAILS' | 'CLAIM_USERNAME';
|
|||||||
|
|
||||||
export const ZSignUpFormV2Schema = z
|
export const ZSignUpFormV2Schema = z
|
||||||
.object({
|
.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),
|
email: z.string().email().min(1),
|
||||||
password: ZPasswordSchema,
|
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
|
url: z
|
||||||
.string()
|
.string()
|
||||||
.trim()
|
.trim()
|
||||||
.toLowerCase()
|
.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-]+$/, {
|
.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(
|
.refine(
|
||||||
@@ -66,10 +69,18 @@ export const ZSignUpFormV2Schema = z
|
|||||||
return !password.includes(name) && !password.includes(email.split('@')[0]);
|
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<string, MessageDescriptor> = {
|
||||||
|
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<typeof ZSignUpFormV2Schema>;
|
export type TSignUpFormV2Schema = z.infer<typeof ZSignUpFormV2Schema>;
|
||||||
|
|
||||||
export type SignUpFormV2Props = {
|
export type SignUpFormV2Props = {
|
||||||
@@ -139,28 +150,20 @@ export const SignUpFormV2 = ({
|
|||||||
} catch (err) {
|
} catch (err) {
|
||||||
const error = AppError.parseError(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', {
|
form.setError('url', {
|
||||||
type: 'manual',
|
type: 'manual',
|
||||||
message: _(msg`This username has already been taken`),
|
message: _(errorMessage),
|
||||||
});
|
|
||||||
} 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',
|
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
toast({
|
toast({
|
||||||
title: _(msg`An unknown error occurred`),
|
title: _(msg`An error occurred`),
|
||||||
description: _(
|
description: _(errorMessage),
|
||||||
msg`We encountered an unknown error while attempting to sign you up. Please try again later.`,
|
|
||||||
),
|
|
||||||
variant: 'destructive',
|
variant: 'destructive',
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -21,8 +21,7 @@ export default trpcNext.createNextApiHandler({
|
|||||||
onError(opts) {
|
onError(opts) {
|
||||||
const { error, path } = opts;
|
const { error, path } = opts;
|
||||||
|
|
||||||
// Currently trialing changes with template and team router only.
|
if (!path) {
|
||||||
if (!path || (!path.startsWith('template') && !path.startsWith('team'))) {
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -2,7 +2,6 @@ import { z } from 'zod';
|
|||||||
|
|
||||||
import { DOCUMENSO_ENCRYPTION_SECONDARY_KEY } from '@documenso/lib/constants/crypto';
|
import { DOCUMENSO_ENCRYPTION_SECONDARY_KEY } from '@documenso/lib/constants/crypto';
|
||||||
import { symmetricEncrypt } from '@documenso/lib/universal/crypto';
|
import { symmetricEncrypt } from '@documenso/lib/universal/crypto';
|
||||||
import type { TEncryptSecondaryDataMutationSchema } from '@documenso/trpc/server/crypto/schema';
|
|
||||||
|
|
||||||
export const ZEncryptedDataSchema = z.object({
|
export const ZEncryptedDataSchema = z.object({
|
||||||
data: z.string(),
|
data: z.string(),
|
||||||
@@ -25,7 +24,7 @@ export type EncryptDataOptions = {
|
|||||||
*
|
*
|
||||||
* @returns The encrypted data.
|
* @returns The encrypted data.
|
||||||
*/
|
*/
|
||||||
export const encryptSecondaryData = ({ data, expiresAt }: TEncryptSecondaryDataMutationSchema) => {
|
export const encryptSecondaryData = ({ data, expiresAt }: EncryptDataOptions) => {
|
||||||
if (!DOCUMENSO_ENCRYPTION_SECONDARY_KEY) {
|
if (!DOCUMENSO_ENCRYPTION_SECONDARY_KEY) {
|
||||||
throw new Error('Missing encryption key');
|
throw new Error('Missing encryption key');
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ import sharp from 'sharp';
|
|||||||
|
|
||||||
import { prisma } from '@documenso/prisma';
|
import { prisma } from '@documenso/prisma';
|
||||||
|
|
||||||
|
import { AppError, AppErrorCode } from '../../errors/app-error';
|
||||||
import type { RequestMetadata } from '../../universal/extract-request-metadata';
|
import type { RequestMetadata } from '../../universal/extract-request-metadata';
|
||||||
|
|
||||||
export type SetAvatarImageOptions = {
|
export type SetAvatarImageOptions = {
|
||||||
@@ -29,7 +30,9 @@ export const setAvatarImage = async ({
|
|||||||
});
|
});
|
||||||
|
|
||||||
if (!user) {
|
if (!user) {
|
||||||
throw new Error('User not found');
|
throw new AppError(AppErrorCode.NOT_FOUND, {
|
||||||
|
message: 'User not found',
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
oldAvatarImageId = user.avatarImageId;
|
oldAvatarImageId = user.avatarImageId;
|
||||||
@@ -47,7 +50,9 @@ export const setAvatarImage = async ({
|
|||||||
});
|
});
|
||||||
|
|
||||||
if (!team) {
|
if (!team) {
|
||||||
throw new Error('Team not found');
|
throw new AppError('TEAM_NOT_FOUND', {
|
||||||
|
statusCode: 404,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
oldAvatarImageId = team.avatarImageId;
|
oldAvatarImageId = team.avatarImageId;
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ import { IdentityProvider, TeamMemberInviteStatus } from '@documenso/prisma/clie
|
|||||||
import { IS_BILLING_ENABLED } from '../../constants/app';
|
import { IS_BILLING_ENABLED } from '../../constants/app';
|
||||||
import { SALT_ROUNDS } from '../../constants/auth';
|
import { SALT_ROUNDS } from '../../constants/auth';
|
||||||
import { AppError, AppErrorCode } from '../../errors/app-error';
|
import { AppError, AppErrorCode } from '../../errors/app-error';
|
||||||
|
import { buildLogger } from '../../utils/logger';
|
||||||
|
|
||||||
export interface CreateUserOptions {
|
export interface CreateUserOptions {
|
||||||
name: string;
|
name: string;
|
||||||
@@ -27,7 +28,7 @@ export const createUser = async ({ name, email, password, signature, url }: Crea
|
|||||||
});
|
});
|
||||||
|
|
||||||
if (userExists) {
|
if (userExists) {
|
||||||
throw new Error('User already exists');
|
throw new AppError(AppErrorCode.ALREADY_EXISTS);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (url) {
|
if (url) {
|
||||||
@@ -134,6 +135,18 @@ export const createUser = async ({ name, email, password, signature, url }: Crea
|
|||||||
return await getStripeCustomerByUser(user).then((session) => session.user);
|
return await getStripeCustomerByUser(user).then((session) => session.user);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error(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,
|
||||||
|
},
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import { prisma } from '@documenso/prisma';
|
|||||||
import { UserSecurityAuditLogType } from '@documenso/prisma/client';
|
import { UserSecurityAuditLogType } from '@documenso/prisma/client';
|
||||||
|
|
||||||
import { SALT_ROUNDS } from '../../constants/auth';
|
import { SALT_ROUNDS } from '../../constants/auth';
|
||||||
|
import { AppError, AppErrorCode } from '../../errors/app-error';
|
||||||
import type { RequestMetadata } from '../../universal/extract-request-metadata';
|
import type { RequestMetadata } from '../../universal/extract-request-metadata';
|
||||||
import { sendResetPassword } from '../auth/send-reset-password';
|
import { sendResetPassword } from '../auth/send-reset-password';
|
||||||
|
|
||||||
@@ -15,7 +16,7 @@ export type ResetPasswordOptions = {
|
|||||||
|
|
||||||
export const resetPassword = async ({ token, password, requestMetadata }: ResetPasswordOptions) => {
|
export const resetPassword = async ({ token, password, requestMetadata }: ResetPasswordOptions) => {
|
||||||
if (!token) {
|
if (!token) {
|
||||||
throw new Error('Invalid token provided. Please try again.');
|
throw new AppError('INVALID_TOKEN');
|
||||||
}
|
}
|
||||||
|
|
||||||
const foundToken = await prisma.passwordResetToken.findFirst({
|
const foundToken = await prisma.passwordResetToken.findFirst({
|
||||||
@@ -28,19 +29,19 @@ export const resetPassword = async ({ token, password, requestMetadata }: ResetP
|
|||||||
});
|
});
|
||||||
|
|
||||||
if (!foundToken) {
|
if (!foundToken) {
|
||||||
throw new Error('Invalid token provided. Please try again.');
|
throw new AppError('INVALID_TOKEN');
|
||||||
}
|
}
|
||||||
|
|
||||||
const now = new Date();
|
const now = new Date();
|
||||||
|
|
||||||
if (now > foundToken.expiry) {
|
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 || '');
|
const isSamePassword = await compare(password, foundToken.User.password || '');
|
||||||
|
|
||||||
if (isSamePassword) {
|
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);
|
const hashedPassword = await hash(password, SALT_ROUNDS);
|
||||||
|
|||||||
@@ -5,6 +5,8 @@ import type { RequestMetadata } from '@documenso/lib/universal/extract-request-m
|
|||||||
import { prisma } from '@documenso/prisma';
|
import { prisma } from '@documenso/prisma';
|
||||||
import { UserSecurityAuditLogType } from '@documenso/prisma/client';
|
import { UserSecurityAuditLogType } from '@documenso/prisma/client';
|
||||||
|
|
||||||
|
import { AppError } from '../../errors/app-error';
|
||||||
|
|
||||||
export type UpdatePasswordOptions = {
|
export type UpdatePasswordOptions = {
|
||||||
userId: number;
|
userId: number;
|
||||||
password: string;
|
password: string;
|
||||||
@@ -26,18 +28,18 @@ export const updatePassword = async ({
|
|||||||
});
|
});
|
||||||
|
|
||||||
if (!user.password) {
|
if (!user.password) {
|
||||||
throw new Error('User has no password');
|
throw new AppError('NO_PASSWORD');
|
||||||
}
|
}
|
||||||
|
|
||||||
const isCurrentPasswordValid = await compare(currentPassword, user.password);
|
const isCurrentPasswordValid = await compare(currentPassword, user.password);
|
||||||
if (!isCurrentPasswordValid) {
|
if (!isCurrentPasswordValid) {
|
||||||
throw new Error('Current password is incorrect.');
|
throw new AppError('INCORRECT_PASSWORD');
|
||||||
}
|
}
|
||||||
|
|
||||||
// Compare the new password with the old password
|
// Compare the new password with the old password
|
||||||
const isSamePassword = await compare(password, user.password);
|
const isSamePassword = await compare(password, user.password);
|
||||||
if (isSamePassword) {
|
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);
|
const hashedNewPassword = await hash(password, SALT_ROUNDS);
|
||||||
|
|||||||
@@ -1,5 +1,3 @@
|
|||||||
import { TRPCError } from '@trpc/server';
|
|
||||||
|
|
||||||
import { findDocuments } from '@documenso/lib/server-only/admin/get-all-documents';
|
import { findDocuments } from '@documenso/lib/server-only/admin/get-all-documents';
|
||||||
import { getEntireDocument } from '@documenso/lib/server-only/admin/get-entire-document';
|
import { getEntireDocument } from '@documenso/lib/server-only/admin/get-entire-document';
|
||||||
import { updateRecipient } from '@documenso/lib/server-only/admin/update-recipient';
|
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 }) => {
|
findDocuments: adminProcedure.input(ZAdminFindDocumentsQuerySchema).query(async ({ input }) => {
|
||||||
const { term, page, perPage } = input;
|
const { term, page, perPage } = input;
|
||||||
|
|
||||||
try {
|
return await findDocuments({ term, page, perPage });
|
||||||
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.',
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}),
|
}),
|
||||||
|
|
||||||
updateUser: adminProcedure
|
updateUser: adminProcedure
|
||||||
@@ -45,16 +34,7 @@ export const adminRouter = router({
|
|||||||
.mutation(async ({ input }) => {
|
.mutation(async ({ input }) => {
|
||||||
const { id, name, email, roles } = input;
|
const { id, name, email, roles } = input;
|
||||||
|
|
||||||
try {
|
return await updateUser({ id, name, email, roles });
|
||||||
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.',
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}),
|
}),
|
||||||
|
|
||||||
updateRecipient: adminProcedure
|
updateRecipient: adminProcedure
|
||||||
@@ -62,38 +42,20 @@ export const adminRouter = router({
|
|||||||
.mutation(async ({ input }) => {
|
.mutation(async ({ input }) => {
|
||||||
const { id, name, email } = input;
|
const { id, name, email } = input;
|
||||||
|
|
||||||
try {
|
return await updateRecipient({ id, name, email });
|
||||||
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.',
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}),
|
}),
|
||||||
|
|
||||||
updateSiteSetting: adminProcedure
|
updateSiteSetting: adminProcedure
|
||||||
.input(ZAdminUpdateSiteSettingMutationSchema)
|
.input(ZAdminUpdateSiteSettingMutationSchema)
|
||||||
.mutation(async ({ ctx, input }) => {
|
.mutation(async ({ ctx, input }) => {
|
||||||
try {
|
const { id, enabled, data } = input;
|
||||||
const { id, enabled, data } = input;
|
|
||||||
|
|
||||||
return await upsertSiteSetting({
|
return await upsertSiteSetting({
|
||||||
id,
|
id,
|
||||||
enabled,
|
enabled,
|
||||||
data,
|
data,
|
||||||
userId: ctx.user.id,
|
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.',
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}),
|
}),
|
||||||
|
|
||||||
resealDocument: adminProcedure
|
resealDocument: adminProcedure
|
||||||
@@ -101,61 +63,34 @@ export const adminRouter = router({
|
|||||||
.mutation(async ({ input }) => {
|
.mutation(async ({ input }) => {
|
||||||
const { id } = 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 });
|
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.',
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}),
|
}),
|
||||||
|
|
||||||
deleteUser: adminProcedure.input(ZAdminDeleteUserMutationSchema).mutation(async ({ input }) => {
|
deleteUser: adminProcedure.input(ZAdminDeleteUserMutationSchema).mutation(async ({ input }) => {
|
||||||
const { id, email } = input;
|
const { id, email } = input;
|
||||||
|
|
||||||
try {
|
const user = await getUserById({ id });
|
||||||
const user = await getUserById({ id });
|
|
||||||
|
|
||||||
if (user.email !== email) {
|
if (user.email !== email) {
|
||||||
throw new Error('Email does not match');
|
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.',
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return await deleteUser({ id });
|
||||||
}),
|
}),
|
||||||
|
|
||||||
deleteDocument: adminProcedure
|
deleteDocument: adminProcedure
|
||||||
.input(ZAdminDeleteDocumentMutationSchema)
|
.input(ZAdminDeleteDocumentMutationSchema)
|
||||||
.mutation(async ({ ctx, input }) => {
|
.mutation(async ({ ctx, input }) => {
|
||||||
const { id, reason } = input;
|
const { id, reason } = input;
|
||||||
try {
|
await sendDeleteEmail({ documentId: id, reason });
|
||||||
await sendDeleteEmail({ documentId: id, reason });
|
|
||||||
|
|
||||||
return await superDeleteDocument({
|
return await superDeleteDocument({
|
||||||
id,
|
id,
|
||||||
requestMetadata: extractNextApiRequestMetadata(ctx.req),
|
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.',
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}),
|
}),
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,5 +1,3 @@
|
|||||||
import { TRPCError } from '@trpc/server';
|
|
||||||
|
|
||||||
import { createApiToken } from '@documenso/lib/server-only/public-api/create-api-token';
|
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 { 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 { getUserTokens } from '@documenso/lib/server-only/public-api/get-all-user-tokens';
|
||||||
@@ -14,78 +12,42 @@ import {
|
|||||||
|
|
||||||
export const apiTokenRouter = router({
|
export const apiTokenRouter = router({
|
||||||
getTokens: authenticatedProcedure.query(async ({ ctx }) => {
|
getTokens: authenticatedProcedure.query(async ({ ctx }) => {
|
||||||
try {
|
return await getUserTokens({ userId: ctx.user.id });
|
||||||
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.',
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}),
|
}),
|
||||||
|
|
||||||
getTokenById: authenticatedProcedure
|
getTokenById: authenticatedProcedure
|
||||||
.input(ZGetApiTokenByIdQuerySchema)
|
.input(ZGetApiTokenByIdQuerySchema)
|
||||||
.query(async ({ input, ctx }) => {
|
.query(async ({ input, ctx }) => {
|
||||||
try {
|
const { id } = input;
|
||||||
const { id } = input;
|
|
||||||
|
|
||||||
return await getApiTokenById({
|
return await getApiTokenById({
|
||||||
id,
|
id,
|
||||||
userId: ctx.user.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.',
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}),
|
}),
|
||||||
|
|
||||||
createToken: authenticatedProcedure
|
createToken: authenticatedProcedure
|
||||||
.input(ZCreateTokenMutationSchema)
|
.input(ZCreateTokenMutationSchema)
|
||||||
.mutation(async ({ input, ctx }) => {
|
.mutation(async ({ input, ctx }) => {
|
||||||
try {
|
const { tokenName, teamId, expirationDate } = input;
|
||||||
const { tokenName, teamId, expirationDate } = input;
|
|
||||||
|
|
||||||
return await createApiToken({
|
return await createApiToken({
|
||||||
userId: ctx.user.id,
|
userId: ctx.user.id,
|
||||||
teamId,
|
teamId,
|
||||||
tokenName,
|
tokenName,
|
||||||
expiresIn: expirationDate,
|
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.',
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}),
|
}),
|
||||||
|
|
||||||
deleteTokenById: authenticatedProcedure
|
deleteTokenById: authenticatedProcedure
|
||||||
.input(ZDeleteTokenByIdMutationSchema)
|
.input(ZDeleteTokenByIdMutationSchema)
|
||||||
.mutation(async ({ input, ctx }) => {
|
.mutation(async ({ input, ctx }) => {
|
||||||
try {
|
const { id, teamId } = input;
|
||||||
const { id, teamId } = input;
|
|
||||||
|
|
||||||
return await deleteTokenById({
|
return await deleteTokenById({
|
||||||
id,
|
id,
|
||||||
teamId,
|
teamId,
|
||||||
userId: ctx.user.id,
|
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.',
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}),
|
}),
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -33,53 +33,30 @@ const NEXT_PUBLIC_DISABLE_SIGNUP = () => env('NEXT_PUBLIC_DISABLE_SIGNUP');
|
|||||||
|
|
||||||
export const authRouter = router({
|
export const authRouter = router({
|
||||||
signup: procedure.input(ZSignUpMutationSchema).mutation(async ({ input }) => {
|
signup: procedure.input(ZSignUpMutationSchema).mutation(async ({ input }) => {
|
||||||
try {
|
if (NEXT_PUBLIC_DISABLE_SIGNUP() === 'true') {
|
||||||
if (NEXT_PUBLIC_DISABLE_SIGNUP() === 'true') {
|
throw new AppError('SIGNUP_DISABLED', {
|
||||||
throw new TRPCError({
|
message: 'Signups are disabled.',
|
||||||
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,
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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
|
verifyPassword: authenticatedProcedure
|
||||||
@@ -104,56 +81,30 @@ export const authRouter = router({
|
|||||||
createPasskey: authenticatedProcedure
|
createPasskey: authenticatedProcedure
|
||||||
.input(ZCreatePasskeyMutationSchema)
|
.input(ZCreatePasskeyMutationSchema)
|
||||||
.mutation(async ({ ctx, input }) => {
|
.mutation(async ({ ctx, input }) => {
|
||||||
try {
|
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
|
||||||
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
|
const verificationResponse = input.verificationResponse as RegistrationResponseJSON;
|
||||||
const verificationResponse = input.verificationResponse as RegistrationResponseJSON;
|
|
||||||
|
|
||||||
return await createPasskey({
|
return await createPasskey({
|
||||||
userId: ctx.user.id,
|
userId: ctx.user.id,
|
||||||
verificationResponse,
|
verificationResponse,
|
||||||
passkeyName: input.passkeyName,
|
passkeyName: input.passkeyName,
|
||||||
requestMetadata: extractNextApiRequestMetadata(ctx.req),
|
requestMetadata: extractNextApiRequestMetadata(ctx.req),
|
||||||
});
|
});
|
||||||
} catch (err) {
|
|
||||||
console.error(err);
|
|
||||||
|
|
||||||
throw err;
|
|
||||||
}
|
|
||||||
}),
|
}),
|
||||||
|
|
||||||
createPasskeyAuthenticationOptions: authenticatedProcedure
|
createPasskeyAuthenticationOptions: authenticatedProcedure
|
||||||
.input(ZCreatePasskeyAuthenticationOptionsMutationSchema)
|
.input(ZCreatePasskeyAuthenticationOptionsMutationSchema)
|
||||||
.mutation(async ({ ctx, input }) => {
|
.mutation(async ({ ctx, input }) => {
|
||||||
try {
|
return await createPasskeyAuthenticationOptions({
|
||||||
return await createPasskeyAuthenticationOptions({
|
userId: ctx.user.id,
|
||||||
userId: ctx.user.id,
|
preferredPasskeyId: input?.preferredPasskeyId,
|
||||||
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.',
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}),
|
}),
|
||||||
|
|
||||||
createPasskeyRegistrationOptions: authenticatedProcedure.mutation(async ({ ctx }) => {
|
createPasskeyRegistrationOptions: authenticatedProcedure.mutation(async ({ ctx }) => {
|
||||||
try {
|
return await createPasskeyRegistrationOptions({
|
||||||
return await createPasskeyRegistrationOptions({
|
userId: ctx.user.id,
|
||||||
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.',
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}),
|
}),
|
||||||
|
|
||||||
createPasskeySigninOptions: procedure.mutation(async ({ ctx }) => {
|
createPasskeySigninOptions: procedure.mutation(async ({ ctx }) => {
|
||||||
@@ -168,80 +119,44 @@ export const authRouter = router({
|
|||||||
|
|
||||||
const [sessionId] = decodeURI(sessionIdToken).split('|');
|
const [sessionId] = decodeURI(sessionIdToken).split('|');
|
||||||
|
|
||||||
try {
|
return await createPasskeySigninOptions({ sessionId });
|
||||||
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.',
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}),
|
}),
|
||||||
|
|
||||||
deletePasskey: authenticatedProcedure
|
deletePasskey: authenticatedProcedure
|
||||||
.input(ZDeletePasskeyMutationSchema)
|
.input(ZDeletePasskeyMutationSchema)
|
||||||
.mutation(async ({ ctx, input }) => {
|
.mutation(async ({ ctx, input }) => {
|
||||||
try {
|
const { passkeyId } = input;
|
||||||
const { passkeyId } = input;
|
|
||||||
|
|
||||||
await deletePasskey({
|
await deletePasskey({
|
||||||
userId: ctx.user.id,
|
userId: ctx.user.id,
|
||||||
passkeyId,
|
passkeyId,
|
||||||
requestMetadata: extractNextApiRequestMetadata(ctx.req),
|
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.',
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}),
|
}),
|
||||||
|
|
||||||
findPasskeys: authenticatedProcedure
|
findPasskeys: authenticatedProcedure
|
||||||
.input(ZFindPasskeysQuerySchema)
|
.input(ZFindPasskeysQuerySchema)
|
||||||
.query(async ({ input, ctx }) => {
|
.query(async ({ input, ctx }) => {
|
||||||
try {
|
const { page, perPage, orderBy } = input;
|
||||||
const { page, perPage, orderBy } = input;
|
|
||||||
|
|
||||||
return await findPasskeys({
|
return await findPasskeys({
|
||||||
page,
|
page,
|
||||||
perPage,
|
perPage,
|
||||||
orderBy,
|
orderBy,
|
||||||
userId: ctx.user.id,
|
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.',
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}),
|
}),
|
||||||
|
|
||||||
updatePasskey: authenticatedProcedure
|
updatePasskey: authenticatedProcedure
|
||||||
.input(ZUpdatePasskeyMutationSchema)
|
.input(ZUpdatePasskeyMutationSchema)
|
||||||
.mutation(async ({ ctx, input }) => {
|
.mutation(async ({ ctx, input }) => {
|
||||||
try {
|
const { passkeyId, name } = input;
|
||||||
const { passkeyId, name } = input;
|
|
||||||
|
|
||||||
await updatePasskey({
|
await updatePasskey({
|
||||||
userId: ctx.user.id,
|
userId: ctx.user.id,
|
||||||
passkeyId,
|
passkeyId,
|
||||||
name,
|
name,
|
||||||
requestMetadata: extractNextApiRequestMetadata(ctx.req),
|
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.',
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}),
|
}),
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -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');
|
|
||||||
}),
|
|
||||||
});
|
|
||||||
@@ -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<typeof ZDecryptDataMutationSchema>;
|
|
||||||
@@ -51,145 +51,82 @@ export const documentRouter = router({
|
|||||||
getDocumentById: authenticatedProcedure
|
getDocumentById: authenticatedProcedure
|
||||||
.input(ZGetDocumentByIdQuerySchema)
|
.input(ZGetDocumentByIdQuerySchema)
|
||||||
.query(async ({ input, ctx }) => {
|
.query(async ({ input, ctx }) => {
|
||||||
try {
|
return await getDocumentById({
|
||||||
return await getDocumentById({
|
...input,
|
||||||
...input,
|
userId: ctx.user.id,
|
||||||
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.',
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}),
|
}),
|
||||||
|
|
||||||
getDocumentByToken: procedure
|
getDocumentByToken: procedure
|
||||||
.input(ZGetDocumentByTokenQuerySchema)
|
.input(ZGetDocumentByTokenQuerySchema)
|
||||||
.query(async ({ input, ctx }) => {
|
.query(async ({ input, ctx }) => {
|
||||||
try {
|
const { token } = input;
|
||||||
const { token } = input;
|
|
||||||
|
|
||||||
return await getDocumentAndSenderByToken({
|
return await getDocumentAndSenderByToken({
|
||||||
token,
|
token,
|
||||||
userId: ctx.user?.id,
|
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.',
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}),
|
}),
|
||||||
|
|
||||||
getDocumentWithDetailsById: authenticatedProcedure
|
getDocumentWithDetailsById: authenticatedProcedure
|
||||||
.input(ZGetDocumentWithDetailsByIdQuerySchema)
|
.input(ZGetDocumentWithDetailsByIdQuerySchema)
|
||||||
.query(async ({ input, ctx }) => {
|
.query(async ({ input, ctx }) => {
|
||||||
try {
|
return await getDocumentWithDetailsById({
|
||||||
return await getDocumentWithDetailsById({
|
...input,
|
||||||
...input,
|
userId: ctx.user.id,
|
||||||
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.',
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}),
|
}),
|
||||||
|
|
||||||
createDocument: authenticatedProcedure
|
createDocument: authenticatedProcedure
|
||||||
.input(ZCreateDocumentMutationSchema)
|
.input(ZCreateDocumentMutationSchema)
|
||||||
.mutation(async ({ input, ctx }) => {
|
.mutation(async ({ input, ctx }) => {
|
||||||
try {
|
const { title, documentDataId, teamId } = input;
|
||||||
const { title, documentDataId, teamId } = input;
|
|
||||||
|
|
||||||
const { remaining } = await getServerLimits({ email: ctx.user.email, teamId });
|
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
if (remaining.documents <= 0) {
|
||||||
throw new TRPCError({
|
throw new TRPCError({
|
||||||
code: 'BAD_REQUEST',
|
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
|
deleteDocument: authenticatedProcedure
|
||||||
.input(ZDeleteDocumentMutationSchema)
|
.input(ZDeleteDocumentMutationSchema)
|
||||||
.mutation(async ({ input, ctx }) => {
|
.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({
|
return await deleteDocument({
|
||||||
id,
|
id,
|
||||||
userId,
|
userId,
|
||||||
teamId,
|
teamId,
|
||||||
requestMetadata: extractNextApiRequestMetadata(ctx.req),
|
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.',
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}),
|
}),
|
||||||
|
|
||||||
moveDocumentToTeam: authenticatedProcedure
|
moveDocumentToTeam: authenticatedProcedure
|
||||||
.input(ZMoveDocumentsToTeamSchema)
|
.input(ZMoveDocumentsToTeamSchema)
|
||||||
.mutation(async ({ input, ctx }) => {
|
.mutation(async ({ input, ctx }) => {
|
||||||
try {
|
const { documentId, teamId } = input;
|
||||||
const { documentId, teamId } = input;
|
const userId = ctx.user.id;
|
||||||
const userId = ctx.user.id;
|
|
||||||
|
|
||||||
return await moveDocumentToTeam({
|
return await moveDocumentToTeam({
|
||||||
documentId,
|
documentId,
|
||||||
teamId,
|
teamId,
|
||||||
userId,
|
userId,
|
||||||
requestMetadata: extractNextApiRequestMetadata(ctx.req),
|
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.',
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}),
|
}),
|
||||||
|
|
||||||
findDocuments: authenticatedProcedure
|
findDocuments: authenticatedProcedure
|
||||||
@@ -199,94 +136,66 @@ export const documentRouter = router({
|
|||||||
|
|
||||||
const { search, teamId, templateId, page, perPage, orderBy, source, status } = input;
|
const { search, teamId, templateId, page, perPage, orderBy, source, status } = input;
|
||||||
|
|
||||||
try {
|
const documents = await findDocuments({
|
||||||
const documents = await findDocuments({
|
userId: user.id,
|
||||||
userId: user.id,
|
teamId,
|
||||||
teamId,
|
templateId,
|
||||||
templateId,
|
search,
|
||||||
search,
|
source,
|
||||||
source,
|
status,
|
||||||
status,
|
page,
|
||||||
page,
|
perPage,
|
||||||
perPage,
|
orderBy,
|
||||||
orderBy,
|
});
|
||||||
});
|
|
||||||
|
|
||||||
return documents;
|
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.',
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}),
|
}),
|
||||||
|
|
||||||
findDocumentAuditLogs: authenticatedProcedure
|
findDocumentAuditLogs: authenticatedProcedure
|
||||||
.input(ZFindDocumentAuditLogsQuerySchema)
|
.input(ZFindDocumentAuditLogsQuerySchema)
|
||||||
.query(async ({ input, ctx }) => {
|
.query(async ({ input, ctx }) => {
|
||||||
try {
|
const { page, perPage, documentId, cursor, filterForRecentActivity, orderBy } = input;
|
||||||
const { page, perPage, documentId, cursor, filterForRecentActivity, orderBy } = input;
|
|
||||||
|
|
||||||
return await findDocumentAuditLogs({
|
return await findDocumentAuditLogs({
|
||||||
page,
|
page,
|
||||||
perPage,
|
perPage,
|
||||||
documentId,
|
documentId,
|
||||||
cursor,
|
cursor,
|
||||||
filterForRecentActivity,
|
filterForRecentActivity,
|
||||||
orderBy,
|
orderBy,
|
||||||
userId: ctx.user.id,
|
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.',
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}),
|
}),
|
||||||
|
|
||||||
// Todo: Add API
|
// Todo: Add API
|
||||||
setSettingsForDocument: authenticatedProcedure
|
setSettingsForDocument: authenticatedProcedure
|
||||||
.input(ZSetSettingsForDocumentMutationSchema)
|
.input(ZSetSettingsForDocumentMutationSchema)
|
||||||
.mutation(async ({ input, ctx }) => {
|
.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) {
|
if (meta.timezone || meta.dateFormat || meta.redirectUrl) {
|
||||||
await upsertDocumentMeta({
|
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,
|
|
||||||
documentId,
|
documentId,
|
||||||
data,
|
dateFormat: meta.dateFormat,
|
||||||
|
timezone: meta.timezone,
|
||||||
|
redirectUrl: meta.redirectUrl,
|
||||||
|
language: meta.language,
|
||||||
|
userId: ctx.user.id,
|
||||||
requestMetadata,
|
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
|
setTitleForDocument: authenticatedProcedure
|
||||||
@@ -296,197 +205,131 @@ export const documentRouter = router({
|
|||||||
|
|
||||||
const userId = ctx.user.id;
|
const userId = ctx.user.id;
|
||||||
|
|
||||||
try {
|
return await updateTitle({
|
||||||
return await updateTitle({
|
title,
|
||||||
title,
|
userId,
|
||||||
userId,
|
teamId,
|
||||||
teamId,
|
documentId,
|
||||||
documentId,
|
requestMetadata: extractNextApiRequestMetadata(ctx.req),
|
||||||
requestMetadata: extractNextApiRequestMetadata(ctx.req),
|
});
|
||||||
});
|
|
||||||
} catch (err) {
|
|
||||||
console.error(err);
|
|
||||||
|
|
||||||
throw err;
|
|
||||||
}
|
|
||||||
}),
|
}),
|
||||||
|
|
||||||
setPasswordForDocument: authenticatedProcedure
|
setPasswordForDocument: authenticatedProcedure
|
||||||
.input(ZSetPasswordForDocumentMutationSchema)
|
.input(ZSetPasswordForDocumentMutationSchema)
|
||||||
.mutation(async ({ input, ctx }) => {
|
.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) {
|
if (!key) {
|
||||||
throw new Error('Missing encryption 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.',
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const securePassword = symmetricEncrypt({
|
||||||
|
data: password,
|
||||||
|
key,
|
||||||
|
});
|
||||||
|
|
||||||
|
await upsertDocumentMeta({
|
||||||
|
documentId,
|
||||||
|
password: securePassword,
|
||||||
|
userId: ctx.user.id,
|
||||||
|
requestMetadata: extractNextApiRequestMetadata(ctx.req),
|
||||||
|
});
|
||||||
}),
|
}),
|
||||||
|
|
||||||
setSigningOrderForDocument: authenticatedProcedure
|
setSigningOrderForDocument: authenticatedProcedure
|
||||||
.input(ZSetSigningOrderForDocumentMutationSchema)
|
.input(ZSetSigningOrderForDocumentMutationSchema)
|
||||||
.mutation(async ({ input, ctx }) => {
|
.mutation(async ({ input, ctx }) => {
|
||||||
try {
|
const { documentId, signingOrder } = input;
|
||||||
const { documentId, signingOrder } = input;
|
|
||||||
|
|
||||||
return await upsertDocumentMeta({
|
return await upsertDocumentMeta({
|
||||||
documentId,
|
documentId,
|
||||||
signingOrder,
|
signingOrder,
|
||||||
userId: ctx.user.id,
|
userId: ctx.user.id,
|
||||||
requestMetadata: extractNextApiRequestMetadata(ctx.req),
|
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.',
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}),
|
}),
|
||||||
|
|
||||||
updateTypedSignatureSettings: authenticatedProcedure
|
updateTypedSignatureSettings: authenticatedProcedure
|
||||||
.input(ZUpdateTypedSignatureSettingsMutationSchema)
|
.input(ZUpdateTypedSignatureSettingsMutationSchema)
|
||||||
.mutation(async ({ input, ctx }) => {
|
.mutation(async ({ input, ctx }) => {
|
||||||
try {
|
const { documentId, teamId, typedSignatureEnabled } = input;
|
||||||
const { documentId, teamId, typedSignatureEnabled } = input;
|
|
||||||
|
|
||||||
const document = await getDocumentById({
|
const document = await getDocumentById({
|
||||||
id: documentId,
|
id: documentId,
|
||||||
teamId,
|
teamId,
|
||||||
userId: ctx.user.id,
|
userId: ctx.user.id,
|
||||||
}).catch(() => null);
|
}).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;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
if (!document) {
|
||||||
throw new TRPCError({
|
throw new TRPCError({
|
||||||
code: 'BAD_REQUEST',
|
code: 'NOT_FOUND',
|
||||||
message:
|
message: 'Document not found',
|
||||||
'We were unable to update the settings for this document. Please try again later.',
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return await upsertDocumentMeta({
|
||||||
|
documentId,
|
||||||
|
typedSignatureEnabled,
|
||||||
|
userId: ctx.user.id,
|
||||||
|
requestMetadata: extractNextApiRequestMetadata(ctx.req),
|
||||||
|
});
|
||||||
}),
|
}),
|
||||||
|
|
||||||
sendDocument: authenticatedProcedure
|
sendDocument: authenticatedProcedure
|
||||||
.input(ZSendDocumentMutationSchema)
|
.input(ZSendDocumentMutationSchema)
|
||||||
.mutation(async ({ input, ctx }) => {
|
.mutation(async ({ input, ctx }) => {
|
||||||
try {
|
const { documentId, teamId, meta } = input;
|
||||||
const { documentId, teamId, meta } = input;
|
|
||||||
|
|
||||||
if (
|
if (
|
||||||
meta.message ||
|
meta.message ||
|
||||||
meta.subject ||
|
meta.subject ||
|
||||||
meta.timezone ||
|
meta.timezone ||
|
||||||
meta.dateFormat ||
|
meta.dateFormat ||
|
||||||
meta.redirectUrl ||
|
meta.redirectUrl ||
|
||||||
meta.distributionMethod ||
|
meta.distributionMethod ||
|
||||||
meta.emailSettings
|
meta.emailSettings
|
||||||
) {
|
) {
|
||||||
await upsertDocumentMeta({
|
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,
|
|
||||||
documentId,
|
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),
|
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
|
resendDocument: authenticatedProcedure
|
||||||
.input(ZResendDocumentMutationSchema)
|
.input(ZResendDocumentMutationSchema)
|
||||||
.mutation(async ({ input, ctx }) => {
|
.mutation(async ({ input, ctx }) => {
|
||||||
try {
|
return await resendDocument({
|
||||||
return await resendDocument({
|
userId: ctx.user.id,
|
||||||
userId: ctx.user.id,
|
...input,
|
||||||
...input,
|
requestMetadata: extractNextApiRequestMetadata(ctx.req),
|
||||||
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.',
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}),
|
}),
|
||||||
|
|
||||||
duplicateDocument: authenticatedProcedure
|
duplicateDocument: authenticatedProcedure
|
||||||
.input(ZGetDocumentByIdQuerySchema)
|
.input(ZGetDocumentByIdQuerySchema)
|
||||||
.mutation(async ({ input, ctx }) => {
|
.mutation(async ({ input, ctx }) => {
|
||||||
try {
|
return await duplicateDocumentById({
|
||||||
return await duplicateDocumentById({
|
userId: ctx.user.id,
|
||||||
userId: ctx.user.id,
|
...input,
|
||||||
...input,
|
});
|
||||||
});
|
|
||||||
} catch (err) {
|
|
||||||
console.log(err);
|
|
||||||
|
|
||||||
throw new TRPCError({
|
|
||||||
code: 'BAD_REQUEST',
|
|
||||||
message: 'We are unable to duplicate this document. Please try again later.',
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}),
|
}),
|
||||||
|
|
||||||
searchDocuments: authenticatedProcedure
|
searchDocuments: authenticatedProcedure
|
||||||
@@ -494,93 +337,64 @@ export const documentRouter = router({
|
|||||||
.query(async ({ input, ctx }) => {
|
.query(async ({ input, ctx }) => {
|
||||||
const { query } = input;
|
const { query } = input;
|
||||||
|
|
||||||
try {
|
const documents = await searchDocumentsWithKeyword({
|
||||||
const documents = await searchDocumentsWithKeyword({
|
query,
|
||||||
query,
|
userId: ctx.user.id,
|
||||||
userId: ctx.user.id,
|
});
|
||||||
});
|
|
||||||
|
|
||||||
return documents;
|
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.',
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}),
|
}),
|
||||||
|
|
||||||
downloadAuditLogs: authenticatedProcedure
|
downloadAuditLogs: authenticatedProcedure
|
||||||
.input(ZDownloadAuditLogsMutationSchema)
|
.input(ZDownloadAuditLogsMutationSchema)
|
||||||
.mutation(async ({ input, ctx }) => {
|
.mutation(async ({ input, ctx }) => {
|
||||||
try {
|
const { documentId, teamId } = input;
|
||||||
const { documentId, teamId } = input;
|
|
||||||
|
|
||||||
const document = await getDocumentById({
|
const document = await getDocumentById({
|
||||||
id: documentId,
|
id: documentId,
|
||||||
userId: ctx.user.id,
|
userId: ctx.user.id,
|
||||||
teamId,
|
teamId,
|
||||||
}).catch(() => null);
|
}).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);
|
|
||||||
|
|
||||||
|
if (!document || (teamId && document.teamId !== teamId)) {
|
||||||
throw new TRPCError({
|
throw new TRPCError({
|
||||||
code: 'BAD_REQUEST',
|
code: 'FORBIDDEN',
|
||||||
message:
|
message: 'You do not have access to this document.',
|
||||||
'We were unable to download the audit logs for this document. Please try again later.',
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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
|
downloadCertificate: authenticatedProcedure
|
||||||
.input(ZDownloadCertificateMutationSchema)
|
.input(ZDownloadCertificateMutationSchema)
|
||||||
.mutation(async ({ input, ctx }) => {
|
.mutation(async ({ input, ctx }) => {
|
||||||
try {
|
const { documentId, teamId } = input;
|
||||||
const { documentId, teamId } = input;
|
|
||||||
|
|
||||||
const document = await getDocumentById({
|
const document = await getDocumentById({
|
||||||
id: documentId,
|
id: documentId,
|
||||||
userId: ctx.user.id,
|
userId: ctx.user.id,
|
||||||
teamId,
|
teamId,
|
||||||
});
|
});
|
||||||
|
|
||||||
if (document.status !== DocumentStatus.COMPLETED) {
|
if (document.status !== DocumentStatus.COMPLETED) {
|
||||||
throw new AppError('DOCUMENT_NOT_COMPLETE');
|
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.',
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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}`,
|
||||||
|
};
|
||||||
}),
|
}),
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,5 +1,3 @@
|
|||||||
import { TRPCError } from '@trpc/server';
|
|
||||||
|
|
||||||
import { getFieldById } from '@documenso/lib/server-only/field/get-field-by-id';
|
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 { removeSignedFieldWithToken } from '@documenso/lib/server-only/field/remove-signed-field-with-token';
|
||||||
import { setFieldsForDocument } from '@documenso/lib/server-only/field/set-fields-for-document';
|
import { setFieldsForDocument } from '@documenso/lib/server-only/field/set-fields-for-document';
|
||||||
@@ -20,33 +18,24 @@ export const fieldRouter = router({
|
|||||||
addFields: authenticatedProcedure
|
addFields: authenticatedProcedure
|
||||||
.input(ZAddFieldsMutationSchema)
|
.input(ZAddFieldsMutationSchema)
|
||||||
.mutation(async ({ input, ctx }) => {
|
.mutation(async ({ input, ctx }) => {
|
||||||
try {
|
const { documentId, fields } = input;
|
||||||
const { documentId, fields } = input;
|
|
||||||
|
|
||||||
return await setFieldsForDocument({
|
return await setFieldsForDocument({
|
||||||
documentId,
|
documentId,
|
||||||
userId: ctx.user.id,
|
userId: ctx.user.id,
|
||||||
fields: fields.map((field) => ({
|
fields: fields.map((field) => ({
|
||||||
id: field.nativeId,
|
id: field.nativeId,
|
||||||
signerEmail: field.signerEmail,
|
signerEmail: field.signerEmail,
|
||||||
type: field.type,
|
type: field.type,
|
||||||
pageNumber: field.pageNumber,
|
pageNumber: field.pageNumber,
|
||||||
pageX: field.pageX,
|
pageX: field.pageX,
|
||||||
pageY: field.pageY,
|
pageY: field.pageY,
|
||||||
pageWidth: field.pageWidth,
|
pageWidth: field.pageWidth,
|
||||||
pageHeight: field.pageHeight,
|
pageHeight: field.pageHeight,
|
||||||
fieldMeta: field.fieldMeta,
|
fieldMeta: field.fieldMeta,
|
||||||
})),
|
})),
|
||||||
requestMetadata: extractNextApiRequestMetadata(ctx.req),
|
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.',
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}),
|
}),
|
||||||
|
|
||||||
addTemplateFields: authenticatedProcedure
|
addTemplateFields: authenticatedProcedure
|
||||||
@@ -54,89 +43,59 @@ export const fieldRouter = router({
|
|||||||
.mutation(async ({ input, ctx }) => {
|
.mutation(async ({ input, ctx }) => {
|
||||||
const { templateId, fields } = input;
|
const { templateId, fields } = input;
|
||||||
|
|
||||||
try {
|
return await setFieldsForTemplate({
|
||||||
return await setFieldsForTemplate({
|
userId: ctx.user.id,
|
||||||
userId: ctx.user.id,
|
templateId,
|
||||||
templateId,
|
fields: fields.map((field) => ({
|
||||||
fields: fields.map((field) => ({
|
id: field.nativeId,
|
||||||
id: field.nativeId,
|
signerEmail: field.signerEmail,
|
||||||
signerEmail: field.signerEmail,
|
type: field.type,
|
||||||
type: field.type,
|
pageNumber: field.pageNumber,
|
||||||
pageNumber: field.pageNumber,
|
pageX: field.pageX,
|
||||||
pageX: field.pageX,
|
pageY: field.pageY,
|
||||||
pageY: field.pageY,
|
pageWidth: field.pageWidth,
|
||||||
pageWidth: field.pageWidth,
|
pageHeight: field.pageHeight,
|
||||||
pageHeight: field.pageHeight,
|
fieldMeta: field.fieldMeta,
|
||||||
fieldMeta: field.fieldMeta,
|
})),
|
||||||
})),
|
});
|
||||||
});
|
|
||||||
} catch (err) {
|
|
||||||
console.error(err);
|
|
||||||
|
|
||||||
throw err;
|
|
||||||
}
|
|
||||||
}),
|
}),
|
||||||
|
|
||||||
signFieldWithToken: procedure
|
signFieldWithToken: procedure
|
||||||
.input(ZSignFieldWithTokenMutationSchema)
|
.input(ZSignFieldWithTokenMutationSchema)
|
||||||
.mutation(async ({ input, ctx }) => {
|
.mutation(async ({ input, ctx }) => {
|
||||||
try {
|
const { token, fieldId, value, isBase64, authOptions } = input;
|
||||||
const { token, fieldId, value, isBase64, authOptions } = input;
|
|
||||||
|
|
||||||
return await signFieldWithToken({
|
return await signFieldWithToken({
|
||||||
token,
|
token,
|
||||||
fieldId,
|
fieldId,
|
||||||
value,
|
value,
|
||||||
isBase64,
|
isBase64,
|
||||||
userId: ctx.user?.id,
|
userId: ctx.user?.id,
|
||||||
authOptions,
|
authOptions,
|
||||||
requestMetadata: extractNextApiRequestMetadata(ctx.req),
|
requestMetadata: extractNextApiRequestMetadata(ctx.req),
|
||||||
});
|
});
|
||||||
} catch (err) {
|
|
||||||
console.error(err);
|
|
||||||
|
|
||||||
throw err;
|
|
||||||
}
|
|
||||||
}),
|
}),
|
||||||
|
|
||||||
removeSignedFieldWithToken: procedure
|
removeSignedFieldWithToken: procedure
|
||||||
.input(ZRemovedSignedFieldWithTokenMutationSchema)
|
.input(ZRemovedSignedFieldWithTokenMutationSchema)
|
||||||
.mutation(async ({ input, ctx }) => {
|
.mutation(async ({ input, ctx }) => {
|
||||||
try {
|
const { token, fieldId } = input;
|
||||||
const { token, fieldId } = input;
|
|
||||||
|
|
||||||
return await removeSignedFieldWithToken({
|
return await removeSignedFieldWithToken({
|
||||||
token,
|
token,
|
||||||
fieldId,
|
fieldId,
|
||||||
requestMetadata: extractNextApiRequestMetadata(ctx.req),
|
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.',
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}),
|
}),
|
||||||
|
|
||||||
getField: authenticatedProcedure.input(ZGetFieldQuerySchema).query(async ({ input, ctx }) => {
|
getField: authenticatedProcedure.input(ZGetFieldQuerySchema).query(async ({ input, ctx }) => {
|
||||||
try {
|
const { fieldId, teamId } = input;
|
||||||
const { fieldId, teamId } = input;
|
|
||||||
|
|
||||||
return await getFieldById({
|
return await getFieldById({
|
||||||
userId: ctx.user.id,
|
userId: ctx.user.id,
|
||||||
teamId,
|
teamId,
|
||||||
fieldId,
|
fieldId,
|
||||||
});
|
});
|
||||||
} catch (err) {
|
|
||||||
console.error(err);
|
|
||||||
|
|
||||||
throw new TRPCError({
|
|
||||||
code: 'BAD_REQUEST',
|
|
||||||
message: 'We were unable to find this field. Please try again.',
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}),
|
}),
|
||||||
|
|
||||||
// This doesn't appear to be used anywhere, and it doesn't seem to support updating template fields
|
// This doesn't appear to be used anywhere, and it doesn't seem to support updating template fields
|
||||||
|
|||||||
@@ -1,5 +1,3 @@
|
|||||||
import { TRPCError } from '@trpc/server';
|
|
||||||
|
|
||||||
import { IS_BILLING_ENABLED } from '@documenso/lib/constants/app';
|
import { IS_BILLING_ENABLED } from '@documenso/lib/constants/app';
|
||||||
import { AppError, AppErrorCode } from '@documenso/lib/errors/app-error';
|
import { AppError, AppErrorCode } from '@documenso/lib/errors/app-error';
|
||||||
import { jobsClient } from '@documenso/lib/jobs/client';
|
import { jobsClient } from '@documenso/lib/jobs/client';
|
||||||
@@ -33,246 +31,122 @@ export const profileRouter = router({
|
|||||||
findUserSecurityAuditLogs: authenticatedProcedure
|
findUserSecurityAuditLogs: authenticatedProcedure
|
||||||
.input(ZFindUserSecurityAuditLogsSchema)
|
.input(ZFindUserSecurityAuditLogsSchema)
|
||||||
.query(async ({ input, ctx }) => {
|
.query(async ({ input, ctx }) => {
|
||||||
try {
|
return await findUserSecurityAuditLogs({
|
||||||
return await findUserSecurityAuditLogs({
|
userId: ctx.user.id,
|
||||||
userId: ctx.user.id,
|
...input,
|
||||||
...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.',
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}),
|
}),
|
||||||
|
|
||||||
getUser: adminProcedure.input(ZRetrieveUserByIdQuerySchema).query(async ({ input }) => {
|
getUser: adminProcedure.input(ZRetrieveUserByIdQuerySchema).query(async ({ input }) => {
|
||||||
try {
|
const { id } = input;
|
||||||
const { id } = input;
|
|
||||||
|
|
||||||
return await getUserById({ id });
|
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.',
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}),
|
}),
|
||||||
|
|
||||||
updateProfile: authenticatedProcedure
|
updateProfile: authenticatedProcedure
|
||||||
.input(ZUpdateProfileMutationSchema)
|
.input(ZUpdateProfileMutationSchema)
|
||||||
.mutation(async ({ input, ctx }) => {
|
.mutation(async ({ input, ctx }) => {
|
||||||
try {
|
const { name, signature } = input;
|
||||||
const { name, signature } = input;
|
|
||||||
|
|
||||||
return await updateProfile({
|
return await updateProfile({
|
||||||
userId: ctx.user.id,
|
userId: ctx.user.id,
|
||||||
name,
|
name,
|
||||||
signature,
|
signature,
|
||||||
requestMetadata: extractNextApiRequestMetadata(ctx.req),
|
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.',
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}),
|
}),
|
||||||
|
|
||||||
updatePublicProfile: authenticatedProcedure
|
updatePublicProfile: authenticatedProcedure
|
||||||
.input(ZUpdatePublicProfileMutationSchema)
|
.input(ZUpdatePublicProfileMutationSchema)
|
||||||
.mutation(async ({ input, ctx }) => {
|
.mutation(async ({ input, ctx }) => {
|
||||||
try {
|
const { url, bio, enabled } = input;
|
||||||
const { url, bio, enabled } = input;
|
|
||||||
|
|
||||||
if (IS_BILLING_ENABLED() && url !== undefined && url.length < 6) {
|
if (IS_BILLING_ENABLED() && url !== undefined && url.length < 6) {
|
||||||
const subscriptions = await getSubscriptionsByUserId({
|
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({
|
|
||||||
userId: ctx.user.id,
|
userId: ctx.user.id,
|
||||||
data: {
|
}).then((subscriptions) =>
|
||||||
url,
|
subscriptions.filter((s) => s.status === SubscriptionStatus.ACTIVE),
|
||||||
bio,
|
);
|
||||||
enabled,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
return { success: true, url: user.url };
|
if (subscriptions.length === 0) {
|
||||||
} catch (err) {
|
throw new AppError(AppErrorCode.PREMIUM_PROFILE_URL, {
|
||||||
console.error(err);
|
message: 'Only subscribers can have a username shorter than 6 characters',
|
||||||
|
});
|
||||||
const error = AppError.parseError(err);
|
|
||||||
|
|
||||||
if (error.code !== AppErrorCode.UNKNOWN_ERROR) {
|
|
||||||
throw error;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
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
|
updatePassword: authenticatedProcedure
|
||||||
.input(ZUpdatePasswordMutationSchema)
|
.input(ZUpdatePasswordMutationSchema)
|
||||||
.mutation(async ({ input, ctx }) => {
|
.mutation(async ({ input, ctx }) => {
|
||||||
try {
|
const { password, currentPassword } = input;
|
||||||
const { password, currentPassword } = input;
|
|
||||||
|
|
||||||
return await updatePassword({
|
return await updatePassword({
|
||||||
userId: ctx.user.id,
|
userId: ctx.user.id,
|
||||||
password,
|
password,
|
||||||
currentPassword,
|
currentPassword,
|
||||||
requestMetadata: extractNextApiRequestMetadata(ctx.req),
|
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,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}),
|
}),
|
||||||
|
|
||||||
forgotPassword: procedure.input(ZForgotPasswordFormSchema).mutation(async ({ input }) => {
|
forgotPassword: procedure.input(ZForgotPasswordFormSchema).mutation(async ({ input }) => {
|
||||||
try {
|
const { email } = input;
|
||||||
const { email } = input;
|
|
||||||
|
|
||||||
return await forgotPassword({
|
return await forgotPassword({
|
||||||
email,
|
email,
|
||||||
});
|
});
|
||||||
} catch (err) {
|
|
||||||
console.error(err);
|
|
||||||
}
|
|
||||||
}),
|
}),
|
||||||
|
|
||||||
resetPassword: procedure.input(ZResetPasswordFormSchema).mutation(async ({ input, ctx }) => {
|
resetPassword: procedure.input(ZResetPasswordFormSchema).mutation(async ({ input, ctx }) => {
|
||||||
try {
|
const { password, token } = input;
|
||||||
const { password, token } = input;
|
|
||||||
|
|
||||||
return await resetPassword({
|
return await resetPassword({
|
||||||
token,
|
token,
|
||||||
password,
|
password,
|
||||||
requestMetadata: extractNextApiRequestMetadata(ctx.req),
|
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,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}),
|
}),
|
||||||
|
|
||||||
sendConfirmationEmail: procedure
|
sendConfirmationEmail: procedure
|
||||||
.input(ZConfirmEmailMutationSchema)
|
.input(ZConfirmEmailMutationSchema)
|
||||||
.mutation(async ({ input }) => {
|
.mutation(async ({ input }) => {
|
||||||
try {
|
const { email } = input;
|
||||||
const { email } = input;
|
|
||||||
|
|
||||||
await jobsClient.triggerJob({
|
await jobsClient.triggerJob({
|
||||||
name: 'send.signup.confirmation.email',
|
name: 'send.signup.confirmation.email',
|
||||||
payload: {
|
payload: {
|
||||||
email,
|
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,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}),
|
}),
|
||||||
|
|
||||||
deleteAccount: authenticatedProcedure.mutation(async ({ ctx }) => {
|
deleteAccount: authenticatedProcedure.mutation(async ({ ctx }) => {
|
||||||
try {
|
return await deleteUser({
|
||||||
return await deleteUser({
|
id: ctx.user.id,
|
||||||
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,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}),
|
}),
|
||||||
|
|
||||||
setProfileImage: authenticatedProcedure
|
setProfileImage: authenticatedProcedure
|
||||||
.input(ZSetProfileImageMutationSchema)
|
.input(ZSetProfileImageMutationSchema)
|
||||||
.mutation(async ({ input, ctx }) => {
|
.mutation(async ({ input, ctx }) => {
|
||||||
try {
|
const { bytes, teamId } = input;
|
||||||
const { bytes, teamId } = input;
|
|
||||||
|
|
||||||
return await setAvatarImage({
|
return await setAvatarImage({
|
||||||
userId: ctx.user.id,
|
userId: ctx.user.id,
|
||||||
teamId,
|
teamId,
|
||||||
bytes,
|
bytes,
|
||||||
requestMetadata: extractNextApiRequestMetadata(ctx.req),
|
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,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}),
|
}),
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,5 +1,3 @@
|
|||||||
import { TRPCError } from '@trpc/server';
|
|
||||||
|
|
||||||
import { completeDocumentWithToken } from '@documenso/lib/server-only/document/complete-document-with-token';
|
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 { rejectDocumentWithToken } from '@documenso/lib/server-only/document/reject-document-with-token';
|
||||||
import { setRecipientsForDocument } from '@documenso/lib/server-only/recipient/set-recipients-for-document';
|
import { setRecipientsForDocument } from '@documenso/lib/server-only/recipient/set-recipients-for-document';
|
||||||
@@ -18,104 +16,68 @@ export const recipientRouter = router({
|
|||||||
addSigners: authenticatedProcedure
|
addSigners: authenticatedProcedure
|
||||||
.input(ZAddSignersMutationSchema)
|
.input(ZAddSignersMutationSchema)
|
||||||
.mutation(async ({ input, ctx }) => {
|
.mutation(async ({ input, ctx }) => {
|
||||||
try {
|
const { documentId, teamId, signers } = input;
|
||||||
const { documentId, teamId, signers } = input;
|
|
||||||
|
|
||||||
return await setRecipientsForDocument({
|
return await setRecipientsForDocument({
|
||||||
userId: ctx.user.id,
|
userId: ctx.user.id,
|
||||||
documentId,
|
documentId,
|
||||||
teamId,
|
teamId,
|
||||||
recipients: signers.map((signer) => ({
|
recipients: signers.map((signer) => ({
|
||||||
id: signer.nativeId,
|
id: signer.nativeId,
|
||||||
email: signer.email,
|
email: signer.email,
|
||||||
name: signer.name,
|
name: signer.name,
|
||||||
role: signer.role,
|
role: signer.role,
|
||||||
signingOrder: signer.signingOrder,
|
signingOrder: signer.signingOrder,
|
||||||
actionAuth: signer.actionAuth,
|
actionAuth: signer.actionAuth,
|
||||||
})),
|
})),
|
||||||
requestMetadata: extractNextApiRequestMetadata(ctx.req),
|
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.',
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}),
|
}),
|
||||||
|
|
||||||
addTemplateSigners: authenticatedProcedure
|
addTemplateSigners: authenticatedProcedure
|
||||||
.input(ZAddTemplateSignersMutationSchema)
|
.input(ZAddTemplateSignersMutationSchema)
|
||||||
.mutation(async ({ input, ctx }) => {
|
.mutation(async ({ input, ctx }) => {
|
||||||
try {
|
const { templateId, signers, teamId } = input;
|
||||||
const { templateId, signers, teamId } = input;
|
|
||||||
|
|
||||||
return await setRecipientsForTemplate({
|
return await setRecipientsForTemplate({
|
||||||
userId: ctx.user.id,
|
userId: ctx.user.id,
|
||||||
teamId,
|
teamId,
|
||||||
templateId,
|
templateId,
|
||||||
recipients: signers.map((signer) => ({
|
recipients: signers.map((signer) => ({
|
||||||
id: signer.nativeId,
|
id: signer.nativeId,
|
||||||
email: signer.email,
|
email: signer.email,
|
||||||
name: signer.name,
|
name: signer.name,
|
||||||
role: signer.role,
|
role: signer.role,
|
||||||
signingOrder: signer.signingOrder,
|
signingOrder: signer.signingOrder,
|
||||||
actionAuth: signer.actionAuth,
|
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.',
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}),
|
}),
|
||||||
|
|
||||||
completeDocumentWithToken: procedure
|
completeDocumentWithToken: procedure
|
||||||
.input(ZCompleteDocumentWithTokenMutationSchema)
|
.input(ZCompleteDocumentWithTokenMutationSchema)
|
||||||
.mutation(async ({ input, ctx }) => {
|
.mutation(async ({ input, ctx }) => {
|
||||||
try {
|
const { token, documentId, authOptions } = input;
|
||||||
const { token, documentId, authOptions } = input;
|
|
||||||
|
|
||||||
return await completeDocumentWithToken({
|
return await completeDocumentWithToken({
|
||||||
token,
|
token,
|
||||||
documentId,
|
documentId,
|
||||||
authOptions,
|
authOptions,
|
||||||
userId: ctx.user?.id,
|
userId: ctx.user?.id,
|
||||||
requestMetadata: extractNextApiRequestMetadata(ctx.req),
|
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.',
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}),
|
}),
|
||||||
|
|
||||||
rejectDocumentWithToken: procedure
|
rejectDocumentWithToken: procedure
|
||||||
.input(ZRejectDocumentWithTokenMutationSchema)
|
.input(ZRejectDocumentWithTokenMutationSchema)
|
||||||
.mutation(async ({ input, ctx }) => {
|
.mutation(async ({ input, ctx }) => {
|
||||||
try {
|
const { token, documentId, reason } = input;
|
||||||
const { token, documentId, reason } = input;
|
|
||||||
|
|
||||||
return await rejectDocumentWithToken({
|
return await rejectDocumentWithToken({
|
||||||
token,
|
token,
|
||||||
documentId,
|
documentId,
|
||||||
reason,
|
reason,
|
||||||
requestMetadata: extractNextApiRequestMetadata(ctx.req),
|
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.',
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}),
|
}),
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
import { adminRouter } from './admin-router/router';
|
import { adminRouter } from './admin-router/router';
|
||||||
import { apiTokenRouter } from './api-token-router/router';
|
import { apiTokenRouter } from './api-token-router/router';
|
||||||
import { authRouter } from './auth-router/router';
|
import { authRouter } from './auth-router/router';
|
||||||
import { cryptoRouter } from './crypto/router';
|
|
||||||
import { documentRouter } from './document-router/router';
|
import { documentRouter } from './document-router/router';
|
||||||
import { fieldRouter } from './field-router/router';
|
import { fieldRouter } from './field-router/router';
|
||||||
import { profileRouter } from './profile-router/router';
|
import { profileRouter } from './profile-router/router';
|
||||||
@@ -16,7 +15,6 @@ import { webhookRouter } from './webhook-router/router';
|
|||||||
|
|
||||||
export const appRouter = router({
|
export const appRouter = router({
|
||||||
auth: authRouter,
|
auth: authRouter,
|
||||||
crypto: cryptoRouter,
|
|
||||||
profile: profileRouter,
|
profile: profileRouter,
|
||||||
document: documentRouter,
|
document: documentRouter,
|
||||||
field: fieldRouter,
|
field: fieldRouter,
|
||||||
|
|||||||
@@ -1,5 +1,3 @@
|
|||||||
import { TRPCError } from '@trpc/server';
|
|
||||||
|
|
||||||
import { createOrGetShareLink } from '@documenso/lib/server-only/share/create-or-get-share-link';
|
import { createOrGetShareLink } from '@documenso/lib/server-only/share/create-or-get-share-link';
|
||||||
|
|
||||||
import { procedure, router } from '../trpc';
|
import { procedure, router } from '../trpc';
|
||||||
@@ -9,27 +7,18 @@ export const shareLinkRouter = router({
|
|||||||
createOrGetShareLink: procedure
|
createOrGetShareLink: procedure
|
||||||
.input(ZCreateOrGetShareLinkMutationSchema)
|
.input(ZCreateOrGetShareLinkMutationSchema)
|
||||||
.mutation(async ({ ctx, input }) => {
|
.mutation(async ({ ctx, input }) => {
|
||||||
try {
|
const { documentId, token } = input;
|
||||||
const { documentId, token } = input;
|
|
||||||
|
|
||||||
if (token) {
|
if (token) {
|
||||||
return await createOrGetShareLink({ documentId, 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 (!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 });
|
||||||
}),
|
}),
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -30,159 +30,153 @@ export const singleplayerRouter = router({
|
|||||||
createSinglePlayerDocument: procedure
|
createSinglePlayerDocument: procedure
|
||||||
.input(ZCreateSinglePlayerDocumentMutationSchema)
|
.input(ZCreateSinglePlayerDocumentMutationSchema)
|
||||||
.mutation(async ({ input }) => {
|
.mutation(async ({ input }) => {
|
||||||
try {
|
const { signer, fields, documentData, documentName, fieldMeta } = input;
|
||||||
const { signer, fields, documentData, documentName, fieldMeta } = input;
|
|
||||||
|
|
||||||
const document = await getFile({
|
const document = await getFile({
|
||||||
data: documentData.data,
|
data: documentData.data,
|
||||||
type: documentData.type,
|
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 { token } = await prisma.$transaction(
|
||||||
const signatureImageAsBase64 = isBase64 ? signer.signature : null;
|
async (tx) => {
|
||||||
const typedSignature = !isBase64 ? signer.signature : null;
|
const token = alphaid();
|
||||||
|
|
||||||
// Update the document with the fields inserted.
|
// Fetch service user who will be the owner of the document.
|
||||||
for (const field of fields) {
|
const serviceUser = await tx.user.findFirstOrThrow({
|
||||||
const isSignatureField = field.type === FieldType.SIGNATURE;
|
where: {
|
||||||
|
email: SERVICE_USER_EMAIL,
|
||||||
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 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(
|
// Create recipient.
|
||||||
async (tx) => {
|
const recipient = await tx.recipient.create({
|
||||||
const token = alphaid();
|
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.
|
// Create fields and signatures.
|
||||||
const serviceUser = await tx.user.findFirstOrThrow({
|
await Promise.all(
|
||||||
where: {
|
fields.map(async (field) => {
|
||||||
email: SERVICE_USER_EMAIL,
|
const insertedField = await tx.field.create({
|
||||||
},
|
data: {
|
||||||
});
|
documentId: document.id,
|
||||||
|
recipientId: recipient.id,
|
||||||
|
...mapField(field, signer),
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
const { id: documentDataId } = await putPdfFile({
|
if (field.type === FieldType.SIGNATURE || field.type === FieldType.FREE_SIGNATURE) {
|
||||||
name: `${documentName}.pdf`,
|
await tx.signature.create({
|
||||||
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({
|
|
||||||
data: {
|
data: {
|
||||||
documentId: document.id,
|
fieldId: insertedField.id,
|
||||||
|
signatureImageAsBase64,
|
||||||
|
typedSignature,
|
||||||
recipientId: recipient.id,
|
recipientId: recipient.id,
|
||||||
...mapField(field, signer),
|
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
|
||||||
if (field.type === FieldType.SIGNATURE || field.type === FieldType.FREE_SIGNATURE) {
|
return { document, token };
|
||||||
await tx.signature.create({
|
},
|
||||||
data: {
|
{
|
||||||
fieldId: insertedField.id,
|
maxWait: 5000,
|
||||||
signatureImageAsBase64,
|
timeout: 30000,
|
||||||
typedSignature,
|
},
|
||||||
recipientId: recipient.id,
|
);
|
||||||
},
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}),
|
|
||||||
);
|
|
||||||
|
|
||||||
return { document, token };
|
const template = createElement(DocumentSelfSignedEmailTemplate, {
|
||||||
},
|
documentName: documentName,
|
||||||
{
|
assetBaseUrl: NEXT_PUBLIC_WEBAPP_URL() || 'http://localhost:3000',
|
||||||
maxWait: 5000,
|
});
|
||||||
timeout: 30000,
|
|
||||||
},
|
|
||||||
);
|
|
||||||
|
|
||||||
const template = createElement(DocumentSelfSignedEmailTemplate, {
|
const [html, text] = await Promise.all([
|
||||||
documentName: documentName,
|
renderEmailWithI18N(template),
|
||||||
assetBaseUrl: NEXT_PUBLIC_WEBAPP_URL() || 'http://localhost:3000',
|
renderEmailWithI18N(template, { plainText: true }),
|
||||||
});
|
]);
|
||||||
|
|
||||||
const [html, text] = await Promise.all([
|
// Send email to signer.
|
||||||
renderEmailWithI18N(template),
|
await mailer.sendMail({
|
||||||
renderEmailWithI18N(template, { plainText: true }),
|
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.
|
return token;
|
||||||
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;
|
|
||||||
}
|
|
||||||
}),
|
}),
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -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 { disableTwoFactorAuthentication } from '@documenso/lib/server-only/2fa/disable-2fa';
|
||||||
import { enableTwoFactorAuthentication } from '@documenso/lib/server-only/2fa/enable-2fa';
|
import { enableTwoFactorAuthentication } from '@documenso/lib/server-only/2fa/enable-2fa';
|
||||||
import { setupTwoFactorAuthentication } from '@documenso/lib/server-only/2fa/setup-2fa';
|
import { setupTwoFactorAuthentication } from '@documenso/lib/server-only/2fa/setup-2fa';
|
||||||
@@ -16,89 +13,44 @@ import {
|
|||||||
|
|
||||||
export const twoFactorAuthenticationRouter = router({
|
export const twoFactorAuthenticationRouter = router({
|
||||||
setup: authenticatedProcedure.mutation(async ({ ctx }) => {
|
setup: authenticatedProcedure.mutation(async ({ ctx }) => {
|
||||||
try {
|
return await setupTwoFactorAuthentication({
|
||||||
return await setupTwoFactorAuthentication({
|
user: ctx.user,
|
||||||
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.',
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}),
|
}),
|
||||||
|
|
||||||
enable: authenticatedProcedure
|
enable: authenticatedProcedure
|
||||||
.input(ZEnableTwoFactorAuthenticationMutationSchema)
|
.input(ZEnableTwoFactorAuthenticationMutationSchema)
|
||||||
.mutation(async ({ ctx, input }) => {
|
.mutation(async ({ ctx, input }) => {
|
||||||
try {
|
const user = ctx.user;
|
||||||
const user = ctx.user;
|
|
||||||
|
|
||||||
const { code } = input;
|
const { code } = input;
|
||||||
|
|
||||||
return await enableTwoFactorAuthentication({
|
return await enableTwoFactorAuthentication({
|
||||||
user,
|
user,
|
||||||
code,
|
code,
|
||||||
requestMetadata: extractNextApiRequestMetadata(ctx.req),
|
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.',
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}),
|
}),
|
||||||
|
|
||||||
disable: authenticatedProcedure
|
disable: authenticatedProcedure
|
||||||
.input(ZDisableTwoFactorAuthenticationMutationSchema)
|
.input(ZDisableTwoFactorAuthenticationMutationSchema)
|
||||||
.mutation(async ({ ctx, input }) => {
|
.mutation(async ({ ctx, input }) => {
|
||||||
try {
|
const user = ctx.user;
|
||||||
const user = ctx.user;
|
|
||||||
|
|
||||||
return await disableTwoFactorAuthentication({
|
return await disableTwoFactorAuthentication({
|
||||||
user,
|
user,
|
||||||
totpCode: input.totpCode,
|
totpCode: input.totpCode,
|
||||||
backupCode: input.backupCode,
|
backupCode: input.backupCode,
|
||||||
requestMetadata: extractNextApiRequestMetadata(ctx.req),
|
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.',
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}),
|
}),
|
||||||
|
|
||||||
viewRecoveryCodes: authenticatedProcedure
|
viewRecoveryCodes: authenticatedProcedure
|
||||||
.input(ZViewRecoveryCodesMutationSchema)
|
.input(ZViewRecoveryCodesMutationSchema)
|
||||||
.mutation(async ({ ctx, input }) => {
|
.mutation(async ({ ctx, input }) => {
|
||||||
try {
|
return await viewBackupCodes({
|
||||||
return await viewBackupCodes({
|
user: ctx.user,
|
||||||
user: ctx.user,
|
token: input.token,
|
||||||
token: input.token,
|
});
|
||||||
});
|
|
||||||
} catch (err) {
|
|
||||||
const error = AppError.parseError(err);
|
|
||||||
|
|
||||||
if (error.code !== 'INCORRECT_TWO_FACTOR_CODE') {
|
|
||||||
console.error(err);
|
|
||||||
}
|
|
||||||
|
|
||||||
throw error;
|
|
||||||
}
|
|
||||||
}),
|
}),
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,5 +1,3 @@
|
|||||||
import { TRPCError } from '@trpc/server';
|
|
||||||
|
|
||||||
import { createWebhook } from '@documenso/lib/server-only/webhooks/create-webhook';
|
import { createWebhook } from '@documenso/lib/server-only/webhooks/create-webhook';
|
||||||
import { deleteWebhookById } from '@documenso/lib/server-only/webhooks/delete-webhook-by-id';
|
import { deleteWebhookById } from '@documenso/lib/server-only/webhooks/delete-webhook-by-id';
|
||||||
import { editWebhook } from '@documenso/lib/server-only/webhooks/edit-webhook';
|
import { editWebhook } from '@documenso/lib/server-only/webhooks/edit-webhook';
|
||||||
@@ -18,16 +16,7 @@ import {
|
|||||||
|
|
||||||
export const webhookRouter = router({
|
export const webhookRouter = router({
|
||||||
getWebhooks: authenticatedProcedure.query(async ({ ctx }) => {
|
getWebhooks: authenticatedProcedure.query(async ({ ctx }) => {
|
||||||
try {
|
return await getWebhooksByUserId(ctx.user.id);
|
||||||
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.',
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}),
|
}),
|
||||||
|
|
||||||
getTeamWebhooks: authenticatedProcedure
|
getTeamWebhooks: authenticatedProcedure
|
||||||
@@ -35,37 +24,19 @@ export const webhookRouter = router({
|
|||||||
.query(async ({ ctx, input }) => {
|
.query(async ({ ctx, input }) => {
|
||||||
const { teamId } = input;
|
const { teamId } = input;
|
||||||
|
|
||||||
try {
|
return await getWebhooksByTeamId(teamId, ctx.user.id);
|
||||||
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.',
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}),
|
}),
|
||||||
|
|
||||||
getWebhookById: authenticatedProcedure
|
getWebhookById: authenticatedProcedure
|
||||||
.input(ZGetWebhookByIdQuerySchema)
|
.input(ZGetWebhookByIdQuerySchema)
|
||||||
.query(async ({ input, ctx }) => {
|
.query(async ({ input, ctx }) => {
|
||||||
try {
|
const { id, teamId } = input;
|
||||||
const { id, teamId } = input;
|
|
||||||
|
|
||||||
return await getWebhookById({
|
return await getWebhookById({
|
||||||
id,
|
id,
|
||||||
userId: ctx.user.id,
|
userId: ctx.user.id,
|
||||||
teamId,
|
teamId,
|
||||||
});
|
});
|
||||||
} catch (err) {
|
|
||||||
console.error(err);
|
|
||||||
|
|
||||||
throw new TRPCError({
|
|
||||||
code: 'BAD_REQUEST',
|
|
||||||
message: 'We were unable to fetch your webhook. Please try again later.',
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}),
|
}),
|
||||||
|
|
||||||
createWebhook: authenticatedProcedure
|
createWebhook: authenticatedProcedure
|
||||||
@@ -73,65 +44,38 @@ export const webhookRouter = router({
|
|||||||
.mutation(async ({ input, ctx }) => {
|
.mutation(async ({ input, ctx }) => {
|
||||||
const { enabled, eventTriggers, secret, webhookUrl, teamId } = input;
|
const { enabled, eventTriggers, secret, webhookUrl, teamId } = input;
|
||||||
|
|
||||||
try {
|
return await createWebhook({
|
||||||
return await createWebhook({
|
enabled,
|
||||||
enabled,
|
secret,
|
||||||
secret,
|
webhookUrl,
|
||||||
webhookUrl,
|
eventTriggers,
|
||||||
eventTriggers,
|
teamId,
|
||||||
teamId,
|
userId: ctx.user.id,
|
||||||
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.',
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}),
|
}),
|
||||||
|
|
||||||
deleteWebhook: authenticatedProcedure
|
deleteWebhook: authenticatedProcedure
|
||||||
.input(ZDeleteWebhookMutationSchema)
|
.input(ZDeleteWebhookMutationSchema)
|
||||||
.mutation(async ({ input, ctx }) => {
|
.mutation(async ({ input, ctx }) => {
|
||||||
try {
|
const { id, teamId } = input;
|
||||||
const { id, teamId } = input;
|
|
||||||
|
|
||||||
return await deleteWebhookById({
|
return await deleteWebhookById({
|
||||||
id,
|
id,
|
||||||
teamId,
|
teamId,
|
||||||
userId: ctx.user.id,
|
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.',
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}),
|
}),
|
||||||
|
|
||||||
editWebhook: authenticatedProcedure
|
editWebhook: authenticatedProcedure
|
||||||
.input(ZEditWebhookMutationSchema)
|
.input(ZEditWebhookMutationSchema)
|
||||||
.mutation(async ({ input, ctx }) => {
|
.mutation(async ({ input, ctx }) => {
|
||||||
try {
|
const { id, teamId, ...data } = input;
|
||||||
const { id, teamId, ...data } = input;
|
|
||||||
|
|
||||||
return await editWebhook({
|
return await editWebhook({
|
||||||
id,
|
id,
|
||||||
data,
|
data,
|
||||||
userId: ctx.user.id,
|
userId: ctx.user.id,
|
||||||
teamId,
|
teamId,
|
||||||
});
|
});
|
||||||
} catch (err) {
|
|
||||||
console.error(err);
|
|
||||||
|
|
||||||
throw new TRPCError({
|
|
||||||
code: 'BAD_REQUEST',
|
|
||||||
message: 'We were unable to create this webhook. Please try again later.',
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}),
|
}),
|
||||||
});
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user