feat: migrate nextjs to rr7

This commit is contained in:
David Nguyen
2025-01-02 15:33:37 +11:00
parent 9183f668d3
commit 383b5f78f0
898 changed files with 31175 additions and 24615 deletions

View File

@ -14,7 +14,6 @@
"@documenso/prisma": "*",
"@tanstack/react-query": "5.59.15",
"@trpc/client": "11.0.0-rc.648",
"@trpc/next": "11.0.0-rc.648",
"@trpc/react-query": "11.0.0-rc.648",
"@trpc/server": "11.0.0-rc.648",
"@ts-rest/core": "^3.30.5",

View File

@ -1,8 +1,5 @@
'use client';
import { useState } from 'react';
import type { QueryClientConfig } from '@tanstack/react-query';
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
import { httpBatchLink, httpLink, splitLink } from '@trpc/client';
import { createTRPCReact } from '@trpc/react-query';
@ -10,6 +7,7 @@ import SuperJSON from 'superjson';
import { getBaseUrl } from '@documenso/lib/universal/get-base-url';
// import { getBaseUrl } from '@documenso/lib/universal/get-base-url';
import type { AppRouter } from '../server/router';
export { getQueryKey } from '@trpc/react-query';
@ -39,27 +37,7 @@ export interface TrpcProviderProps {
}
export function TrpcProvider({ children, headers }: TrpcProviderProps) {
let queryClientConfig: QueryClientConfig | undefined;
const isDevelopingOffline =
typeof window !== 'undefined' &&
window.location.hostname === 'localhost' &&
!window.navigator.onLine;
if (isDevelopingOffline) {
queryClientConfig = {
defaultOptions: {
queries: {
networkMode: 'always',
},
mutations: {
networkMode: 'always',
},
},
};
}
const [queryClient] = useState(() => new QueryClient(queryClientConfig));
const [queryClient] = useState(() => new QueryClient());
const [trpcClient] = useState(() =>
trpc.createClient({

View File

@ -1,3 +1,5 @@
import { DocumentStatus } from '@prisma/client';
import { AppError, AppErrorCode } from '@documenso/lib/errors/app-error';
import { findDocuments } from '@documenso/lib/server-only/admin/get-all-documents';
import { getEntireDocument } from '@documenso/lib/server-only/admin/get-entire-document';
@ -11,8 +13,6 @@ import { deleteUser } from '@documenso/lib/server-only/user/delete-user';
import { disableUser } from '@documenso/lib/server-only/user/disable-user';
import { enableUser } from '@documenso/lib/server-only/user/enable-user';
import { getUserById } from '@documenso/lib/server-only/user/get-user-by-id';
import { extractNextApiRequestMetadata } from '@documenso/lib/universal/extract-request-metadata';
import { DocumentStatus } from '@documenso/prisma/client';
import { adminProcedure, router } from '../trpc';
import {
@ -117,7 +117,7 @@ export const adminRouter = router({
return await superDeleteDocument({
id,
requestMetadata: extractNextApiRequestMetadata(ctx.req),
requestMetadata: ctx.metadata.requestMetadata,
});
}),
});

View File

@ -1,22 +1,13 @@
import type { RegistrationResponseJSON } from '@simplewebauthn/types';
import { TRPCError } from '@trpc/server';
import { parse } from 'cookie-es';
import { env } from 'next-runtime-env';
import { IS_BILLING_ENABLED } from '@documenso/lib/constants/app';
import { formatSecureCookieName } from '@documenso/lib/constants/auth';
import { AppError, AppErrorCode } from '@documenso/lib/errors/app-error';
import { jobsClient } from '@documenso/lib/jobs/client';
import { ErrorCode } from '@documenso/lib/next-auth/error-codes';
import { createPasskey } from '@documenso/lib/server-only/auth/create-passkey';
import { createPasskeyAuthenticationOptions } from '@documenso/lib/server-only/auth/create-passkey-authentication-options';
import { createPasskeyRegistrationOptions } from '@documenso/lib/server-only/auth/create-passkey-registration-options';
import { createPasskeySigninOptions } from '@documenso/lib/server-only/auth/create-passkey-signin-options';
import { deletePasskey } from '@documenso/lib/server-only/auth/delete-passkey';
import { findPasskeys } from '@documenso/lib/server-only/auth/find-passkeys';
import { compareSync } from '@documenso/lib/server-only/auth/hash';
import { updatePasskey } from '@documenso/lib/server-only/auth/update-passkey';
import { createUser } from '@documenso/lib/server-only/user/create-user';
import { nanoid } from '@documenso/lib/universal/id';
import { authenticatedProcedure, procedure, router } from '../trpc';
import {
@ -24,60 +15,10 @@ import {
ZCreatePasskeyMutationSchema,
ZDeletePasskeyMutationSchema,
ZFindPasskeysQuerySchema,
ZSignUpMutationSchema,
ZUpdatePasskeyMutationSchema,
ZVerifyPasswordMutationSchema,
} from './schema';
const NEXT_PUBLIC_DISABLE_SIGNUP = () => env('NEXT_PUBLIC_DISABLE_SIGNUP');
export const authRouter = router({
signup: procedure.input(ZSignUpMutationSchema).mutation(async ({ input }) => {
if (NEXT_PUBLIC_DISABLE_SIGNUP() === 'true') {
throw new AppError('SIGNUP_DISABLED', {
message: 'Signups are disabled.',
});
}
const { name, email, password, signature, url } = input;
if (IS_BILLING_ENABLED() && url && url.length < 6) {
throw new AppError(AppErrorCode.PREMIUM_PROFILE_URL, {
message: 'Only subscribers can have a username shorter than 6 characters',
});
}
const user = await createUser({ name, email, password, signature, url });
await jobsClient.triggerJob({
name: 'send.signup.confirmation.email',
payload: {
email: user.email,
},
});
return user;
}),
verifyPassword: authenticatedProcedure
.input(ZVerifyPasswordMutationSchema)
.mutation(({ ctx, input }) => {
const user = ctx.user;
const { password } = input;
if (!user.password) {
throw new TRPCError({
code: 'BAD_REQUEST',
message: ErrorCode.INCORRECT_PASSWORD,
});
}
const valid = compareSync(password, user.password);
return valid;
}),
createPasskey: authenticatedProcedure
.input(ZCreatePasskeyMutationSchema)
.mutation(async ({ ctx, input }) => {
@ -107,20 +48,17 @@ export const authRouter = router({
});
}),
createPasskeySigninOptions: procedure.mutation(async ({ ctx }) => {
const cookies = parse(ctx.req.headers.cookie ?? '');
const sessionIdToken =
cookies[formatSecureCookieName('__Host-next-auth.csrf-token')] ||
cookies[formatSecureCookieName('next-auth.csrf-token')];
if (!sessionIdToken) {
throw new Error('Missing CSRF token');
}
createPasskeySigninOptions: procedure.mutation(async () => {
const sessionIdToken = nanoid(16);
const [sessionId] = decodeURI(sessionIdToken).split('|');
return await createPasskeySigninOptions({ sessionId });
const options = await createPasskeySigninOptions({ sessionId });
return {
options,
sessionId,
};
}),
deletePasskey: authenticatedProcedure

View File

@ -71,5 +71,3 @@ export const ZFindPasskeysQuerySchema = ZFindSearchParamsSchema.extend({
});
export type TSignUpMutationSchema = z.infer<typeof ZSignUpMutationSchema>;
export const ZVerifyPasswordMutationSchema = ZSignUpMutationSchema.pick({ password: true });

View File

@ -1,45 +1,40 @@
import type { Context } from 'hono';
import { z } from 'zod';
import { getServerSession } from '@documenso/lib/next-auth/get-server-session';
import type { SessionUser } from '@documenso/auth/server/lib/session/session';
import { getSession } from '@documenso/auth/server/lib/utils/get-session';
import type { ApiRequestMetadata } from '@documenso/lib/universal/extract-request-metadata';
import { extractNextApiRequestMetadata } from '@documenso/lib/universal/extract-request-metadata';
import { extractRequestMetadata } from '@documenso/lib/universal/extract-request-metadata';
import type { Session } from '@documenso/prisma/client';
import type { CreateNextContextOptions } from './adapters/next';
type CreateTrpcContext = CreateNextContextOptions & {
requestSource: 'apiV1' | 'apiV2' | 'app';
type CreateTrpcContextOptions = {
c: Context;
requestSource: 'app' | 'apiV1' | 'apiV2';
};
export const createTrpcContext = async ({
req,
res,
c,
requestSource,
}: Omit<CreateTrpcContext, 'info'>) => {
const { session, user } = await getServerSession({ req, res });
}: CreateTrpcContextOptions): Promise<TrpcContext> => {
const { session, user } = await getSession(c);
const req = c.req.raw;
const metadata: ApiRequestMetadata = {
requestMetadata: extractNextApiRequestMetadata(req),
requestMetadata: extractRequestMetadata(req),
source: requestSource,
auth: null,
};
const rawTeamId = req.headers.get('x-team-id') || undefined;
const teamId = z.coerce
.number()
.optional()
.catch(() => undefined)
.parse(req.headers['x-team-id']);
.parse(rawTeamId);
if (!session) {
return {
session: null,
user: null,
teamId,
req,
metadata,
};
}
if (!user) {
if (!session || !user) {
return {
session: null,
user: null,
@ -58,4 +53,17 @@ export const createTrpcContext = async ({
};
};
export type TrpcContext = Awaited<ReturnType<typeof createTrpcContext>>;
export type TrpcContext = (
| {
session: null;
user: null;
}
| {
session: Session;
user: SessionUser;
}
) & {
teamId: number | undefined;
req: Request;
metadata: ApiRequestMetadata;
};

View File

@ -1,3 +1,4 @@
import { DocumentDataType, DocumentStatus } from '@prisma/client';
import { TRPCError } from '@trpc/server';
import { DateTime } from 'luxon';
@ -17,14 +18,16 @@ import { findDocuments } from '@documenso/lib/server-only/document/find-document
import { getDocumentById } from '@documenso/lib/server-only/document/get-document-by-id';
import { getDocumentAndSenderByToken } from '@documenso/lib/server-only/document/get-document-by-token';
import { getDocumentWithDetailsById } from '@documenso/lib/server-only/document/get-document-with-details-by-id';
import type { GetStatsInput } from '@documenso/lib/server-only/document/get-stats';
import { getStats } from '@documenso/lib/server-only/document/get-stats';
import { moveDocumentToTeam } from '@documenso/lib/server-only/document/move-document-to-team';
import { resendDocument } from '@documenso/lib/server-only/document/resend-document';
import { searchDocumentsWithKeyword } from '@documenso/lib/server-only/document/search-documents-with-keyword';
import { sendDocument } from '@documenso/lib/server-only/document/send-document';
import { updateDocument } from '@documenso/lib/server-only/document/update-document';
import { getTeamById } from '@documenso/lib/server-only/team/get-team';
import { symmetricEncrypt } from '@documenso/lib/universal/crypto';
import { getPresignPostUrl } from '@documenso/lib/universal/upload/server-actions';
import { DocumentDataType, DocumentStatus } from '@documenso/prisma/client';
import { authenticatedProcedure, procedure, router } from '../trpc';
import {
@ -39,6 +42,8 @@ import {
ZDuplicateDocumentRequestSchema,
ZDuplicateDocumentResponseSchema,
ZFindDocumentAuditLogsQuerySchema,
ZFindDocumentsInternalRequestSchema,
ZFindDocumentsInternalResponseSchema,
ZFindDocumentsRequestSchema,
ZFindDocumentsResponseSchema,
ZGenericSuccessResponse,
@ -124,6 +129,74 @@ export const documentRouter = router({
return documents;
}),
/**
* Internal endpoint for /documents page to additionally return getStats.
*
* @private
*/
findDocumentsInternal: authenticatedProcedure
.input(ZFindDocumentsInternalRequestSchema)
.output(ZFindDocumentsInternalResponseSchema)
.query(async ({ input, ctx }) => {
const { user, teamId } = ctx;
const {
query,
templateId,
page,
perPage,
orderByDirection,
orderByColumn,
source,
status,
period,
senderIds,
} = input;
const getStatOptions: GetStatsInput = {
user,
period,
search: query,
};
if (teamId) {
const team = await getTeamById({ userId: user.id, teamId });
getStatOptions.team = {
teamId: team.id,
teamEmail: team.teamEmail?.email,
senderIds,
currentTeamMemberRole: team.currentTeamMember?.role,
currentUserEmail: user.email,
userId: user.id,
};
}
const [stats, documents] = await Promise.all([
getStats(getStatOptions),
findDocuments({
userId: user.id,
teamId,
query,
templateId,
page,
perPage,
source,
status,
period,
senderIds,
orderBy: orderByColumn
? { column: orderByColumn, direction: orderByDirection }
: undefined,
}),
]);
return {
...documents,
stats,
};
}),
/**
* @public
*

View File

@ -1,3 +1,11 @@
import {
DocumentDistributionMethod,
DocumentSigningOrder,
DocumentSource,
DocumentStatus,
DocumentVisibility,
FieldType,
} from '@prisma/client';
import { z } from 'zod';
import { VALID_DATE_FORMAT_VALUES } from '@documenso/lib/constants/date-formats';
@ -23,14 +31,7 @@ import {
import { ZFieldAndMetaSchema } from '@documenso/lib/types/field-meta';
import { ZFindResultResponse, ZFindSearchParamsSchema } from '@documenso/lib/types/search-params';
import { isValidRedirectUrl } from '@documenso/lib/utils/is-valid-redirect-url';
import {
DocumentDistributionMethod,
DocumentSigningOrder,
DocumentSource,
DocumentStatus,
DocumentVisibility,
FieldType,
} from '@documenso/prisma/client';
import { ExtendedDocumentStatus } from '@documenso/prisma/types/extended-document-status';
import { ZCreateRecipientSchema } from '../recipient-router/schema';
@ -131,6 +132,25 @@ export const ZFindDocumentsResponseSchema = ZFindResultResponse.extend({
export type TFindDocumentsResponse = z.infer<typeof ZFindDocumentsResponseSchema>;
export const ZFindDocumentsInternalRequestSchema = ZFindDocumentsRequestSchema.extend({
period: z.enum(['7d', '14d', '30d']).optional(),
senderIds: z.array(z.number()).optional(),
status: z.nativeEnum(ExtendedDocumentStatus).optional(),
});
export const ZFindDocumentsInternalResponseSchema = ZFindResultResponse.extend({
data: ZDocumentManySchema.array(),
stats: z.object({
[ExtendedDocumentStatus.DRAFT]: z.number(),
[ExtendedDocumentStatus.PENDING]: z.number(),
[ExtendedDocumentStatus.COMPLETED]: z.number(),
[ExtendedDocumentStatus.INBOX]: z.number(),
[ExtendedDocumentStatus.ALL]: z.number(),
}),
});
export type TFindDocumentsInternalResponse = z.infer<typeof ZFindDocumentsInternalResponseSchema>;
export const ZFindDocumentAuditLogsQuerySchema = ZFindSearchParamsSchema.extend({
documentId: z.number().min(1),
cursor: z.string().optional(),

View File

@ -9,7 +9,6 @@ import { setFieldsForTemplate } from '@documenso/lib/server-only/field/set-field
import { signFieldWithToken } from '@documenso/lib/server-only/field/sign-field-with-token';
import { updateDocumentFields } from '@documenso/lib/server-only/field/update-document-fields';
import { updateTemplateFields } from '@documenso/lib/server-only/field/update-template-fields';
import { extractNextApiRequestMetadata } from '@documenso/lib/universal/extract-request-metadata';
import { ZGenericSuccessResponse, ZSuccessResponseSchema } from '../document-router/schema';
import { authenticatedProcedure, procedure, router } from '../trpc';
@ -456,7 +455,7 @@ export const fieldRouter = router({
isBase64,
userId: ctx.user?.id,
authOptions,
requestMetadata: extractNextApiRequestMetadata(ctx.req),
requestMetadata: ctx.metadata.requestMetadata,
});
}),
@ -471,7 +470,7 @@ export const fieldRouter = router({
return await removeSignedFieldWithToken({
token,
fieldId,
requestMetadata: extractNextApiRequestMetadata(ctx.req),
requestMetadata: ctx.metadata.requestMetadata,
});
}),
});

View File

@ -1,3 +1,4 @@
import { FieldType } from '@prisma/client';
import { z } from 'zod';
import { ZRecipientActionAuthSchema } from '@documenso/lib/types/document-auth';
@ -10,7 +11,6 @@ import {
ZFieldWidthSchema,
} from '@documenso/lib/types/field';
import { ZFieldAndMetaSchema, ZFieldMetaSchema } from '@documenso/lib/types/field-meta';
import { FieldType } from '@documenso/prisma/client';
const ZCreateFieldSchema = ZFieldAndMetaSchema.and(
z.object({

View File

@ -1,28 +1,20 @@
import { SubscriptionStatus } from '@prisma/client';
import { IS_BILLING_ENABLED } from '@documenso/lib/constants/app';
import { AppError, AppErrorCode } from '@documenso/lib/errors/app-error';
import { jobsClient } from '@documenso/lib/jobs/client';
import { AppError } from '@documenso/lib/errors/app-error';
import { setAvatarImage } from '@documenso/lib/server-only/profile/set-avatar-image';
import { getSubscriptionsByUserId } from '@documenso/lib/server-only/subscription/get-subscriptions-by-user-id';
import { deleteUser } from '@documenso/lib/server-only/user/delete-user';
import { findUserSecurityAuditLogs } from '@documenso/lib/server-only/user/find-user-security-audit-logs';
import { forgotPassword } from '@documenso/lib/server-only/user/forgot-password';
import { getUserById } from '@documenso/lib/server-only/user/get-user-by-id';
import { resetPassword } from '@documenso/lib/server-only/user/reset-password';
import { updatePassword } from '@documenso/lib/server-only/user/update-password';
import { updateProfile } from '@documenso/lib/server-only/user/update-profile';
import { updatePublicProfile } from '@documenso/lib/server-only/user/update-public-profile';
import { extractNextApiRequestMetadata } from '@documenso/lib/universal/extract-request-metadata';
import { SubscriptionStatus } from '@documenso/prisma/client';
import { adminProcedure, authenticatedProcedure, procedure, router } from '../trpc';
import { adminProcedure, authenticatedProcedure, router } from '../trpc';
import {
ZConfirmEmailMutationSchema,
ZFindUserSecurityAuditLogsSchema,
ZForgotPasswordFormSchema,
ZResetPasswordFormSchema,
ZRetrieveUserByIdQuerySchema,
ZSetProfileImageMutationSchema,
ZUpdatePasswordMutationSchema,
ZUpdateProfileMutationSchema,
ZUpdatePublicProfileMutationSchema,
} from './schema';
@ -69,7 +61,7 @@ export const profileRouter = router({
);
if (subscriptions.length === 0) {
throw new AppError(AppErrorCode.PREMIUM_PROFILE_URL, {
throw new AppError('PREMIUM_PROFILE_URL', {
message: 'Only subscribers can have a username shorter than 6 characters',
});
}
@ -87,50 +79,6 @@ export const profileRouter = router({
return { success: true, url: user.url };
}),
updatePassword: authenticatedProcedure
.input(ZUpdatePasswordMutationSchema)
.mutation(async ({ input, ctx }) => {
const { password, currentPassword } = input;
return await updatePassword({
userId: ctx.user.id,
password,
currentPassword,
requestMetadata: ctx.metadata.requestMetadata,
});
}),
forgotPassword: procedure.input(ZForgotPasswordFormSchema).mutation(async ({ input }) => {
const { email } = input;
return await forgotPassword({
email,
});
}),
resetPassword: procedure.input(ZResetPasswordFormSchema).mutation(async ({ input, ctx }) => {
const { password, token } = input;
return await resetPassword({
token,
password,
requestMetadata: extractNextApiRequestMetadata(ctx.req),
});
}),
sendConfirmationEmail: procedure
.input(ZConfirmEmailMutationSchema)
.mutation(async ({ input }) => {
const { email } = input;
await jobsClient.triggerJob({
name: 'send.signup.confirmation.email',
payload: {
email,
},
});
}),
deleteAccount: authenticatedProcedure.mutation(async ({ ctx }) => {
return await deleteUser({
id: ctx.user.id,

View File

@ -1,7 +1,5 @@
import { z } from 'zod';
import { ZCurrentPasswordSchema, ZPasswordSchema } from '../auth-router/schema';
export const MAX_PROFILE_BIO_LENGTH = 256;
export const ZFindUserSecurityAuditLogsSchema = z.object({
@ -45,32 +43,6 @@ export const ZUpdatePublicProfileMutationSchema = z.object({
export type TUpdatePublicProfileMutationSchema = z.infer<typeof ZUpdatePublicProfileMutationSchema>;
export const ZUpdatePasswordMutationSchema = z.object({
currentPassword: ZCurrentPasswordSchema,
password: ZPasswordSchema,
});
export type TUpdatePasswordMutationSchema = z.infer<typeof ZUpdatePasswordMutationSchema>;
export const ZForgotPasswordFormSchema = z.object({
email: z.string().email().min(1),
});
export type TForgotPasswordFormSchema = z.infer<typeof ZForgotPasswordFormSchema>;
export const ZResetPasswordFormSchema = z.object({
password: ZPasswordSchema,
token: z.string().min(1),
});
export type TResetPasswordFormSchema = z.infer<typeof ZResetPasswordFormSchema>;
export const ZConfirmEmailMutationSchema = z.object({
email: z.string().email().min(1),
});
export type TConfirmEmailMutationSchema = z.infer<typeof ZConfirmEmailMutationSchema>;
export const ZSetProfileImageMutationSchema = z.object({
bytes: z.string().nullish(),
teamId: z.number().min(1).nullish(),

View File

@ -9,7 +9,6 @@ import { setDocumentRecipients } from '@documenso/lib/server-only/recipient/set-
import { setTemplateRecipients } from '@documenso/lib/server-only/recipient/set-template-recipients';
import { updateDocumentRecipients } from '@documenso/lib/server-only/recipient/update-document-recipients';
import { updateTemplateRecipients } from '@documenso/lib/server-only/recipient/update-template-recipients';
import { extractNextApiRequestMetadata } from '@documenso/lib/universal/extract-request-metadata';
import { ZGenericSuccessResponse, ZSuccessResponseSchema } from '../document-router/schema';
import { authenticatedProcedure, procedure, router } from '../trpc';
@ -444,7 +443,7 @@ export const recipientRouter = router({
documentId,
authOptions,
userId: ctx.user?.id,
requestMetadata: extractNextApiRequestMetadata(ctx.req),
requestMetadata: ctx.metadata.requestMetadata,
});
}),
@ -460,7 +459,7 @@ export const recipientRouter = router({
token,
documentId,
reason,
requestMetadata: extractNextApiRequestMetadata(ctx.req),
requestMetadata: ctx.metadata.requestMetadata,
});
}),
});

View File

@ -1,3 +1,4 @@
import { RecipientRole } from '@prisma/client';
import { z } from 'zod';
import {
@ -6,7 +7,6 @@ import {
ZRecipientActionAuthTypesSchema,
} from '@documenso/lib/types/document-auth';
import { ZRecipientLiteSchema, ZRecipientSchema } from '@documenso/lib/types/recipient';
import { RecipientRole } from '@documenso/prisma/client';
export const ZGetRecipientRequestSchema = z.object({
recipientId: z.number(),

View File

@ -1,9 +1,9 @@
import { DocumentVisibility, TeamMemberRole } from '@prisma/client';
import { z } from 'zod';
import { SUPPORTED_LANGUAGE_CODES } from '@documenso/lib/constants/i18n';
import { PROTECTED_TEAM_URLS } from '@documenso/lib/constants/teams';
import { ZFindSearchParamsSchema } from '@documenso/lib/types/search-params';
import { DocumentVisibility, TeamMemberRole } from '@documenso/prisma/client';
import { ZUpdatePublicProfileMutationSchema } from '../profile-router/schema';

View File

@ -1,3 +1,5 @@
import type { Document } from '@prisma/client';
import { getServerLimits } from '@documenso/ee/server-only/limits/server';
import { AppError, AppErrorCode } from '@documenso/lib/errors/app-error';
import { getDocumentWithDetailsById } from '@documenso/lib/server-only/document/get-document-with-details-by-id';
@ -20,7 +22,6 @@ import { getTemplateById } from '@documenso/lib/server-only/template/get-templat
import { moveTemplateToTeam } from '@documenso/lib/server-only/template/move-template-to-team';
import { toggleTemplateDirectLink } from '@documenso/lib/server-only/template/toggle-template-direct-link';
import { updateTemplate } from '@documenso/lib/server-only/template/update-template';
import type { Document } from '@documenso/prisma/client';
import { ZGenericSuccessResponse, ZSuccessResponseSchema } from '../document-router/schema';
import { authenticatedProcedure, maybeAuthenticatedProcedure, router } from '../trpc';

View File

@ -1,3 +1,4 @@
import { DocumentSigningOrder, DocumentVisibility, TemplateType } from '@prisma/client';
import { z } from 'zod';
import { ZDocumentSchema } from '@documenso/lib/types/document';
@ -12,8 +13,7 @@ import {
ZTemplateManySchema,
ZTemplateSchema,
} from '@documenso/lib/types/template';
import { DocumentSigningOrder, DocumentVisibility, TemplateType } from '@documenso/prisma/client';
import { TemplateDirectLinkSchema } from '@documenso/prisma/generated/zod';
import { TemplateDirectLinkSchema } from '@documenso/prisma/generated/zod/modelSchema/TemplateDirectLinkSchema';
import {
ZDocumentMetaDateFormatSchema,

View File

@ -6,7 +6,6 @@ import { AppError, genericErrorCodeToTrpcErrorCodeMap } from '@documenso/lib/err
import { isAdmin } from '@documenso/lib/next-auth/guards/is-admin';
import { getApiTokenByToken } from '@documenso/lib/server-only/public-api/get-api-token-by-token';
import type { ApiRequestMetadata } from '@documenso/lib/universal/extract-request-metadata';
import { extractNextApiRequestMetadata } from '@documenso/lib/universal/extract-request-metadata';
import type { TrpcContext } from './context';
@ -67,7 +66,7 @@ const t = initTRPC
* Middlewares
*/
export const authenticatedMiddleware = t.middleware(async ({ ctx, next }) => {
const authorizationHeader = ctx.req.headers.authorization;
const authorizationHeader = ctx.req.headers.get('authorization');
// Taken from `authenticatedMiddleware` in `@documenso/api/v1/middleware/authenticated.ts`.
if (authorizationHeader) {
@ -131,8 +130,6 @@ export const authenticatedMiddleware = t.middleware(async ({ ctx, next }) => {
});
export const maybeAuthenticatedMiddleware = t.middleware(async ({ ctx, next }) => {
const requestMetadata = extractNextApiRequestMetadata(ctx.req);
return await next({
ctx: {
...ctx,
@ -147,7 +144,6 @@ export const maybeAuthenticatedMiddleware = t.middleware(async ({ ctx, next }) =
email: ctx.user.email,
}
: undefined,
requestMetadata,
auth: ctx.session ? 'session' : null,
} satisfies ApiRequestMetadata,
},

View File

@ -1,7 +1,6 @@
import { WebhookTriggerEvents } from '@prisma/client';
import { z } from 'zod';
import { WebhookTriggerEvents } from '@documenso/prisma/client';
export const ZGetTeamWebhooksQuerySchema = z.object({
teamId: z.number(),
});

View File

@ -0,0 +1,43 @@
import type { ErrorHandlerOptions } from '@trpc/server/unstable-core-do-not-import';
import { AppError, AppErrorCode } from '@documenso/lib/errors/app-error';
import { env } from '@documenso/lib/utils/env';
import { buildLogger } from '@documenso/lib/utils/logger';
const logger = buildLogger();
// Parameters<NonNullable<Parameters<typeof trpcServer>[0]['onError']>>[0], // :-)
export const handleTrpcRouterError = (
{ error, path }: Pick<ErrorHandlerOptions<undefined>, 'error' | 'path'>,
source: 'trpc' | 'apiV1' | 'apiV2',
) => {
// Always log the error on production for now.
if (env('NODE_ENV') !== 'development') {
console.error(error);
}
const appError = AppError.parseError(error.cause || error);
const isAppError = error.cause instanceof AppError;
// Only log AppErrors that are explicitly set to 500 or the error code
// is in the errorCodesToAlertOn list.
const isLoggableAppError =
isAppError && (appError.statusCode === 500 || errorCodesToAlertOn.includes(appError.code));
// Only log TRPC errors that are in the `errorCodesToAlertOn` list and is
// not an AppError.
const isLoggableTrpcError = !isAppError && errorCodesToAlertOn.includes(error.code);
if (isLoggableAppError || isLoggableTrpcError) {
logger.error(error, {
method: path,
context: {
source,
appError: AppError.toJSON(appError),
},
});
}
};
const errorCodesToAlertOn = [AppErrorCode.UNKNOWN_ERROR, 'INTERNAL_SERVER_ERROR'];