From 824f4a070af536092c6826e14d910ecd2af2b849 Mon Sep 17 00:00:00 2001 From: David Nguyen Date: Wed, 27 Mar 2024 20:31:08 +0800 Subject: [PATCH] feat: demo --- apps/web/next.config.js | 3 +- apps/web/package.json | 1 + apps/web/src/app/layout.tsx | 3 + package-lock.json | 22 ++++++ .../document/find-document-audit-logs.ts | 24 +++++- packages/lib/utils/document-audit-logs.ts | 3 +- packages/lib/utils/logger.ts | 16 ++++ .../trpc/server/document-router/router.ts | 75 +++++++++++++++++-- 8 files changed, 136 insertions(+), 11 deletions(-) create mode 100644 packages/lib/utils/logger.ts diff --git a/apps/web/next.config.js b/apps/web/next.config.js index d670dab47..41feed1f9 100644 --- a/apps/web/next.config.js +++ b/apps/web/next.config.js @@ -1,4 +1,5 @@ /* eslint-disable @typescript-eslint/no-var-requires */ +const { withAxiom } = require('next-axiom'); const fs = require('fs'); const path = require('path'); const { version } = require('./package.json'); @@ -91,4 +92,4 @@ const config = { }, }; -module.exports = config; +module.exports = withAxiom(config); diff --git a/apps/web/package.json b/apps/web/package.json index 4f6617d1e..484659740 100644 --- a/apps/web/package.json +++ b/apps/web/package.json @@ -33,6 +33,7 @@ "micro": "^10.0.1", "next": "14.0.3", "next-auth": "4.24.5", + "next-axiom": "^1.1.1", "next-plausible": "^3.10.1", "next-themes": "^0.2.1", "perfect-freehand": "^1.2.0", diff --git a/apps/web/src/app/layout.tsx b/apps/web/src/app/layout.tsx index 7753e1e53..0f3d1607f 100644 --- a/apps/web/src/app/layout.tsx +++ b/apps/web/src/app/layout.tsx @@ -2,6 +2,7 @@ import { Suspense } from 'react'; import { Caveat, Inter } from 'next/font/google'; +import { AxiomWebVitals } from 'next-axiom'; import { PublicEnvScript } from 'next-runtime-env'; import { FeatureFlagProvider } from '@documenso/lib/client-only/providers/feature-flag'; @@ -71,6 +72,8 @@ export default async function RootLayout({ children }: { children: React.ReactNo + + diff --git a/package-lock.json b/package-lock.json index 3dc4e9776..78a99b7a4 100644 --- a/package-lock.json +++ b/package-lock.json @@ -111,6 +111,7 @@ "micro": "^10.0.1", "next": "14.0.3", "next-auth": "4.24.5", + "next-axiom": "^1.1.1", "next-plausible": "^3.10.1", "next-themes": "^0.2.1", "perfect-freehand": "^1.2.0", @@ -16668,6 +16669,22 @@ } } }, + "node_modules/next-axiom": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/next-axiom/-/next-axiom-1.1.1.tgz", + "integrity": "sha512-0r/TJ+/zetD+uDc7B+2E7WpC86hEtQ1U+DuWYrP/JNmUz+ZdPFbrZgzOSqaZ6TwYbXP56VVlPfYwq1YsKHTHYQ==", + "dependencies": { + "remeda": "^1.29.0", + "whatwg-fetch": "^3.6.2" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "next": ">=13.4", + "react": ">=18.0.0" + } + }, "node_modules/next-contentlayer": { "version": "0.3.4", "resolved": "https://registry.npmjs.org/next-contentlayer/-/next-contentlayer-0.3.4.tgz", @@ -22936,6 +22953,11 @@ "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==" }, + "node_modules/whatwg-fetch": { + "version": "3.6.20", + "resolved": "https://registry.npmjs.org/whatwg-fetch/-/whatwg-fetch-3.6.20.tgz", + "integrity": "sha512-EqhiFU6daOA8kpjOWTL0olhVOF3i7OrFzSYiGsEMB8GcXS+RrzauAERX65xMeNWVqxA6HXH2m69Z9LaKKdisfg==" + }, "node_modules/whatwg-url": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", diff --git a/packages/lib/server-only/document/find-document-audit-logs.ts b/packages/lib/server-only/document/find-document-audit-logs.ts index 4f423ce8c..d0800a4d9 100644 --- a/packages/lib/server-only/document/find-document-audit-logs.ts +++ b/packages/lib/server-only/document/find-document-audit-logs.ts @@ -3,8 +3,11 @@ import { prisma } from '@documenso/prisma'; import type { DocumentAuditLog } from '@documenso/prisma/client'; import type { Prisma } from '@documenso/prisma/client'; +import { AppError } from '../../errors/app-error'; +import type { TDocumentAuditLog } from '../../types/document-audit-logs'; import { DOCUMENT_AUDIT_LOG_TYPE } from '../../types/document-audit-logs'; import { parseDocumentAuditLogData } from '../../utils/document-audit-logs'; +import { buildServerLogger } from '../../utils/logger'; export interface FindDocumentAuditLogsOptions { userId: number; @@ -97,7 +100,26 @@ export const findDocumentAuditLogs = async ({ let nextCursor: string | undefined = undefined; - const parsedData = data.map((auditLog) => parseDocumentAuditLogData(auditLog)); + let parsedData: TDocumentAuditLog[] = []; + + try { + parsedData = data.map((auditLog) => parseDocumentAuditLogData(auditLog)); + } catch (err) { + const error = AppError.parseError(err); + + if (error.code === 'MIGRATION_REQUIRED') { + const logger = buildServerLogger(); + + logger.error('findDocumentAuditLogs', { + level: 'ALERT', + error, + }); + + void logger.flush(); + } + + throw error; + } if (parsedData.length > perPage) { const nextItem = parsedData.pop(); diff --git a/packages/lib/utils/document-audit-logs.ts b/packages/lib/utils/document-audit-logs.ts index 65ffb2817..3c5f01c97 100644 --- a/packages/lib/utils/document-audit-logs.ts +++ b/packages/lib/utils/document-audit-logs.ts @@ -9,6 +9,7 @@ import type { } from '@documenso/prisma/client'; import { RECIPIENT_ROLES_DESCRIPTION } from '../constants/recipient-roles'; +import { AppError } from '../errors/app-error'; import type { TDocumentAuditLog, TDocumentAuditLogDocumentMetaDiffSchema, @@ -69,7 +70,7 @@ export const parseDocumentAuditLogData = (auditLog: DocumentAuditLog): TDocument // Handle any required migrations here. if (!data.success) { console.error(data.error); - throw new Error('Migration required'); + throw new AppError('MIGRATION_REQUIRED'); } return data.data; diff --git a/packages/lib/utils/logger.ts b/packages/lib/utils/logger.ts new file mode 100644 index 000000000..ad46e6bb6 --- /dev/null +++ b/packages/lib/utils/logger.ts @@ -0,0 +1,16 @@ +import type { LoggerConfig } from 'next-axiom'; +import { Logger } from 'next-axiom'; + +/** + * For usage in server-side code. + * + * When used in a server component, you must flush the logs. + * + * https://github.com/axiomhq/next-axiom?tab=readme-ov-file#server-components + */ +export const buildServerLogger = (config?: LoggerConfig) => { + return new Logger({ + source: 'server', + ...config, + }); +}; diff --git a/packages/trpc/server/document-router/router.ts b/packages/trpc/server/document-router/router.ts index 70cf15291..02734f902 100644 --- a/packages/trpc/server/document-router/router.ts +++ b/packages/trpc/server/document-router/router.ts @@ -16,6 +16,7 @@ import { sendDocument } from '@documenso/lib/server-only/document/send-document' import { updateTitle } from '@documenso/lib/server-only/document/update-title'; import { symmetricEncrypt } from '@documenso/lib/universal/crypto'; import { extractNextApiRequestMetadata } from '@documenso/lib/universal/extract-request-metadata'; +import { buildServerLogger } from '@documenso/lib/utils/logger'; import { authenticatedProcedure, procedure, router } from '../trpc'; import { @@ -32,6 +33,12 @@ import { ZSetTitleForDocumentMutationSchema, } from './schema'; +const logger = buildServerLogger({ + args: { + context: 'trpcDocumentRouter', + }, +}); + export const documentRouter = router({ getDocumentById: authenticatedProcedure .input(ZGetDocumentByIdQuerySchema) @@ -42,6 +49,10 @@ export const documentRouter = router({ userId: ctx.user.id, }); } catch (err) { + logger.error('getDocumentById', { + error: err, + }); + console.error(err); throw new TRPCError({ @@ -59,6 +70,10 @@ export const documentRouter = router({ token, }); } catch (err) { + logger.error('getDocumentByToken', { + error: err, + }); + console.error(err); throw new TRPCError({ @@ -77,6 +92,10 @@ export const documentRouter = router({ userId: ctx.user.id, }); } catch (err) { + logger.error('getDocumentWithDetailsById', { + error: err, + }); + console.error(err); throw new TRPCError({ @@ -110,6 +129,10 @@ export const documentRouter = router({ requestMetadata: extractNextApiRequestMetadata(ctx.req), }); } catch (err) { + logger.error('createDocument', { + error: err, + }); + if (err instanceof TRPCError) { throw err; } @@ -136,6 +159,10 @@ export const documentRouter = router({ requestMetadata: extractNextApiRequestMetadata(ctx.req), }); } catch (err) { + logger.error('deleteDocument', { + error: err, + }); + console.error(err); throw new TRPCError({ @@ -161,6 +188,10 @@ export const documentRouter = router({ userId: ctx.user.id, }); } catch (err) { + logger.error('findDocumentAuditLogs', { + error: err, + }); + console.error(err); throw new TRPCError({ @@ -177,13 +208,21 @@ export const documentRouter = router({ const userId = ctx.user.id; - return await updateTitle({ - title, - userId, - teamId, - documentId, - requestMetadata: extractNextApiRequestMetadata(ctx.req), - }); + try { + return await updateTitle({ + title, + userId, + teamId, + documentId, + requestMetadata: extractNextApiRequestMetadata(ctx.req), + }); + } catch (err) { + logger.error('setTitleForDocument', { + error: err, + }); + + console.error(err); + } }), setPasswordForDocument: authenticatedProcedure @@ -210,6 +249,10 @@ export const documentRouter = router({ requestMetadata: extractNextApiRequestMetadata(ctx.req), }); } catch (err) { + logger.error('setPasswordForDocument', { + error: err, + }); + console.error(err); throw new TRPCError({ @@ -245,6 +288,10 @@ export const documentRouter = router({ requestMetadata: extractNextApiRequestMetadata(ctx.req), }); } catch (err) { + logger.error('sendDocument', { + error: err, + }); + console.error(err); throw new TRPCError({ @@ -264,6 +311,10 @@ export const documentRouter = router({ requestMetadata: extractNextApiRequestMetadata(ctx.req), }); } catch (err) { + logger.error('resendDocument', { + error: err, + }); + console.error(err); throw new TRPCError({ @@ -282,6 +333,10 @@ export const documentRouter = router({ ...input, }); } catch (err) { + logger.error('duplicateDocument', { + error: err, + }); + console.log(err); throw new TRPCError({ @@ -302,7 +357,11 @@ export const documentRouter = router({ userId: ctx.user.id, }); return documents; - } catch (error) { + } catch (err) { + logger.error('searchDocuments', { + error: err, + }); + throw new TRPCError({ code: 'BAD_REQUEST', message: 'We are unable to search for documents. Please try again later.',