From 7487399123015cdc05c42c938c9268839f0ba369 Mon Sep 17 00:00:00 2001 From: David Nguyen Date: Mon, 30 Jun 2025 19:46:32 +1000 Subject: [PATCH] feat: add more api logs (#1870) Adds more detailed API logging using Pino --- .env.example | 4 +- .gitignore | 3 + package-lock.json | 137 ------ packages/api/v1/implementation.ts | 445 +++++++++++------- packages/api/v1/middleware/authenticated.ts | 44 +- packages/lib/package.json | 1 - packages/lib/types/api-logs.ts | 29 ++ .../lib/universal/extract-request-metadata.ts | 14 +- packages/lib/utils/logger-legacy.ts | 112 ----- packages/lib/utils/logger.ts | 46 +- .../admin-router/create-admin-organisation.ts | 8 +- .../admin-router/create-stripe-customer.ts | 8 +- .../admin-router/create-subscription-claim.ts | 6 +- .../admin-router/delete-subscription-claim.ts | 8 +- .../admin-router/get-admin-organisation.ts | 8 +- packages/trpc/server/admin-router/router.ts | 108 ++++- .../admin-router/update-admin-organisation.ts | 8 +- .../admin-router/update-subscription-claim.ts | 6 +- .../trpc/server/api-token-router/router.ts | 13 + packages/trpc/server/auth-router/router.ts | 12 + .../server/billing/create-subscription.ts | 7 + packages/trpc/server/billing/get-invoices.ts | 6 + .../trpc/server/billing/get-subscription.ts | 6 + .../server/billing/manage-subscription.ts | 6 + packages/trpc/server/context.ts | 19 +- .../trpc/server/document-router/router.ts | 72 ++- .../server/document-router/update-document.ts | 6 + .../update-embedding-document.ts | 6 + .../update-embedding-template.ts | 6 + packages/trpc/server/field-router/router.ts | 96 ++++ packages/trpc/server/folder-router/router.ts | 78 ++- .../create-organisation-group.ts | 6 + .../create-organisation-member-invites.ts | 6 + .../create-organisation.ts | 6 + .../delete-organisation-group.ts | 7 + .../delete-organisation-member-invites.ts | 7 + .../delete-organisation-member.ts | 7 + .../delete-organisation-members.ts | 7 + .../delete-organisation.ts | 6 + .../find-organisation-groups.ts | 6 + .../find-organisation-member-invites.ts | 6 + .../organisation-router/get-organisation.ts | 6 + .../organisation-router/leave-organisation.ts | 7 +- .../resend-organisation-member-invite.ts | 7 + .../update-organisation-group.ts | 6 + .../update-organisation-members.ts | 7 + .../update-organisation-settings.ts | 6 + .../update-organisation.ts | 7 +- packages/trpc/server/profile-router/router.ts | 15 +- .../trpc/server/recipient-router/router.ts | 96 ++++ .../get-document-internal-url-for-qr-code.ts | 6 + .../trpc/server/share-link-router/router.ts | 6 + .../server/team-router/create-team-groups.ts | 7 + .../server/team-router/create-team-members.ts | 7 + .../trpc/server/team-router/create-team.ts | 6 + .../server/team-router/delete-team-group.ts | 7 + .../server/team-router/delete-team-member.ts | 7 + .../trpc/server/team-router/delete-team.ts | 6 + .../server/team-router/find-team-groups.ts | 7 + .../server/team-router/find-team-members.ts | 6 + .../trpc/server/team-router/find-teams.ts | 6 + .../server/team-router/get-team-members.ts | 6 + packages/trpc/server/team-router/get-team.ts | 10 +- packages/trpc/server/team-router/router.ts | 50 +- .../server/team-router/update-team-group.ts | 9 + .../server/team-router/update-team-member.ts | 7 + .../team-router/update-team-settings.ts | 6 + .../trpc/server/team-router/update-team.ts | 6 + .../trpc/server/template-router/router.ts | 76 ++- packages/trpc/server/trpc.ts | 84 +++- packages/trpc/server/webhook-router/router.ts | 33 ++ packages/trpc/utils/trpc-error-handler.ts | 26 +- packages/tsconfig/process-env.d.ts | 4 +- turbo.json | 2 +- 74 files changed, 1395 insertions(+), 544 deletions(-) create mode 100644 packages/lib/types/api-logs.ts delete mode 100644 packages/lib/utils/logger-legacy.ts diff --git a/.env.example b/.env.example index 15b0b3f5c..693f1d8a5 100644 --- a/.env.example +++ b/.env.example @@ -127,4 +127,6 @@ E2E_TEST_AUTHENTICATE_USER_EMAIL="testuser@mail.com" E2E_TEST_AUTHENTICATE_USER_PASSWORD="test_Password123" # [[LOGGER]] -NEXT_PRIVATE_LOGGER_HONEY_BADGER_API_KEY= +# OPTIONAL: The file to save the logger output to. Will disable stdout if provided. +NEXT_PRIVATE_LOGGER_FILE_PATH= + diff --git a/.gitignore b/.gitignore index b95fcc7d2..41ccab438 100644 --- a/.gitignore +++ b/.gitignore @@ -50,3 +50,6 @@ yarn-error.log* !.vscode/tasks.json !.vscode/launch.json !.vscode/extensions.json + +# logs +logs.json \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index 4ae9c4e1d..405ed6350 100644 --- a/package-lock.json +++ b/package-lock.json @@ -3073,36 +3073,6 @@ "integrity": "sha512-lhqDEAvWixy3bZ+UOYbPwUbBkwBq5C1LAJ/xPC8Oi+lL54oyakv/npbA0aU2hgCsx/1NUd4IBvV03+aUBWxerw==", "license": "MIT" }, - "node_modules/@honeybadger-io/core": { - "version": "6.7.0", - "resolved": "https://registry.npmjs.org/@honeybadger-io/core/-/core-6.7.0.tgz", - "integrity": "sha512-bEXRe2UVbfr9q3434/2eO3AHguUT0froYEqrHfTPphR4Aw6+AlFac0YE8elqDZqUSgRQ6m1OXqxmq/HOF+W6LQ==", - "license": "MIT", - "dependencies": { - "json-nd": "^1.0.0", - "stacktrace-parser": "^0.1.10" - }, - "engines": { - "node": ">=14" - } - }, - "node_modules/@honeybadger-io/js": { - "version": "6.11.0", - "resolved": "https://registry.npmjs.org/@honeybadger-io/js/-/js-6.11.0.tgz", - "integrity": "sha512-nSibKUr9ccrs6Jb3Ql7uO/4MdEEv3ONGP1CrD0w3zSMHUvQKHe43NPYfESA7btxjrf9PVeV+m6ETP/193BSILg==", - "license": "MIT", - "dependencies": { - "@honeybadger-io/core": "^6.7.0", - "@types/aws-lambda": "^8.10.89", - "@types/express": "^4.17.13" - }, - "bin": { - "honeybadger-checkins-sync": "scripts/check-ins-sync-bin.js" - }, - "engines": { - "node": ">=14" - } - }, "node_modules/@hono/node-server": { "version": "1.14.2", "resolved": "https://registry.npmjs.org/@hono/node-server/-/node-server-1.14.2.tgz", @@ -11715,16 +11685,6 @@ "@babel/types": "^7.20.7" } }, - "node_modules/@types/body-parser": { - "version": "1.19.5", - "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.5.tgz", - "integrity": "sha512-fB3Zu92ucau0iQ0JMCFQE7b/dv8Ot07NI3KaZIkIUNXq82k4eBAqUaneXfleGY9JWskeS9y+u0nXMyspcuQrCg==", - "license": "MIT", - "dependencies": { - "@types/connect": "*", - "@types/node": "*" - } - }, "node_modules/@types/bunyan": { "version": "1.8.11", "resolved": "https://registry.npmjs.org/@types/bunyan/-/bunyan-1.8.11.tgz", @@ -11845,30 +11805,6 @@ "@types/estree": "*" } }, - "node_modules/@types/express": { - "version": "4.17.22", - "resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.22.tgz", - "integrity": "sha512-eZUmSnhRX9YRSkplpz0N+k6NljUUn5l3EWZIKZvYzhvMphEuNiyyy1viH/ejgt66JWgALwC/gtSUAeQKtSwW/w==", - "license": "MIT", - "dependencies": { - "@types/body-parser": "*", - "@types/express-serve-static-core": "^4.17.33", - "@types/qs": "*", - "@types/serve-static": "*" - } - }, - "node_modules/@types/express-serve-static-core": { - "version": "4.19.6", - "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.19.6.tgz", - "integrity": "sha512-N4LZ2xG7DatVqhCZzOGb1Yi5lMbXSZcmdLDe9EzSndPV2HpWYWzRbaerl2n27irrm94EPpprqa8KpskPT085+A==", - "license": "MIT", - "dependencies": { - "@types/node": "*", - "@types/qs": "*", - "@types/range-parser": "*", - "@types/send": "*" - } - }, "node_modules/@types/formidable": { "version": "2.0.6", "resolved": "https://registry.npmjs.org/@types/formidable/-/formidable-2.0.6.tgz", @@ -11898,12 +11834,6 @@ "hoist-non-react-statics": "^3.3.0" } }, - "node_modules/@types/http-errors": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/@types/http-errors/-/http-errors-2.0.4.tgz", - "integrity": "sha512-D0CFMMtydbJAegzOyHjtiKPLlvnm3iTZyZRSZoLq2mRhDdmLfIWOCYPfQJ4cu2erKghU++QvjcUjp/5h7hESpA==", - "license": "MIT" - }, "node_modules/@types/istanbul-lib-coverage": { "version": "2.0.6", "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.6.tgz", @@ -11977,12 +11907,6 @@ "@types/node": "*" } }, - "node_modules/@types/mime": { - "version": "1.3.5", - "resolved": "https://registry.npmjs.org/@types/mime/-/mime-1.3.5.tgz", - "integrity": "sha512-/pyBZWSLD2n0dcHE3hq8s8ZvcETHtEuF+3E7XVt0Ig2nvsVQXdghHVcEkIWjy9A0wKfTn97a/PSDYohKIlnP/w==", - "license": "MIT" - }, "node_modules/@types/minimist": { "version": "1.2.5", "resolved": "https://registry.npmjs.org/@types/minimist/-/minimist-1.2.5.tgz", @@ -12070,12 +11994,6 @@ "integrity": "sha512-gNMvNH49DJ7OJYv+KAKn0Xp45p8PLl6zo2YnvDIbTd4J6MER2BmWN49TG7n9LvkyihINxeKW8+3bfS2yDC9dzQ==", "license": "MIT" }, - "node_modules/@types/qs": { - "version": "6.14.0", - "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.14.0.tgz", - "integrity": "sha512-eOunJqu0K1923aExK6y8p6fsihYEn/BYuQ4g0CxAAgFc4b/ZLN4CrsRZ55srTdqoiLzU2B2evC+apEIxprEzkQ==", - "license": "MIT" - }, "node_modules/@types/ramda": { "version": "0.30.2", "resolved": "https://registry.npmjs.org/@types/ramda/-/ramda-0.30.2.tgz", @@ -12085,12 +12003,6 @@ "types-ramda": "^0.30.1" } }, - "node_modules/@types/range-parser": { - "version": "1.2.7", - "resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.7.tgz", - "integrity": "sha512-hKormJbkJqzQGhziax5PItDUTMAM9uE2XXQmM37dyd4hVM+5aVl7oVxMVUiVQn2oCQFN/LKCZdvSM0pFRqbSmQ==", - "license": "MIT" - }, "node_modules/@types/react": { "version": "18.3.5", "resolved": "https://registry.npmjs.org/@types/react/-/react-18.3.5.tgz", @@ -12124,27 +12036,6 @@ "integrity": "sha512-wWKOClTTiizcZhXnPY4wikVAwmdYHp8q6DmC+EJUzAMsycb7HB32Kh9RN4+0gExjmPmZSAQjgURXIGATPegAvA==", "license": "MIT" }, - "node_modules/@types/send": { - "version": "0.17.4", - "resolved": "https://registry.npmjs.org/@types/send/-/send-0.17.4.tgz", - "integrity": "sha512-x2EM6TJOybec7c52BX0ZspPodMsQUd5L6PRwOunVyVUhXiBSKf3AezDL8Dgvgt5o0UfKNfuA0eMLr2wLT4AiBA==", - "license": "MIT", - "dependencies": { - "@types/mime": "^1", - "@types/node": "*" - } - }, - "node_modules/@types/serve-static": { - "version": "1.15.7", - "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.15.7.tgz", - "integrity": "sha512-W8Ym+h8nhuRwaKPaDw34QUkwsGi6Rc4yYqvKFo5rm2FUEhCFbzVWrxXUxuKK8TASjWsysJY0nsmNCGhCOIsrOw==", - "license": "MIT", - "dependencies": { - "@types/http-errors": "*", - "@types/node": "*", - "@types/send": "*" - } - }, "node_modules/@types/shimmer": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/@types/shimmer/-/shimmer-1.2.0.tgz", @@ -21899,12 +21790,6 @@ "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", "license": "MIT" }, - "node_modules/json-nd": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/json-nd/-/json-nd-1.0.0.tgz", - "integrity": "sha512-8TIp0HZAY0VVrwRQJJPb4+nOTSPoOWZeEKBTLizUfQO4oym5Fc/MKqN8vEbLCxcyxDf2vwNxOQ1q84O49GWPyQ==", - "license": "BSD-3-Clause" - }, "node_modules/json-parse-even-better-errors": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", @@ -31491,27 +31376,6 @@ "integrity": "sha512-+L3ccpzibovGXFK+Ap/f8LOS0ahMrHTf3xu7mMLSpEGU0EO9ucaysSylKo9eRDFNhWve/y275iPmIZ4z39a9iA==", "license": "MIT" }, - "node_modules/stacktrace-parser": { - "version": "0.1.11", - "resolved": "https://registry.npmjs.org/stacktrace-parser/-/stacktrace-parser-0.1.11.tgz", - "integrity": "sha512-WjlahMgHmCJpqzU8bIBy4qtsZdU9lRlcZE3Lvyej6t4tuOuv1vk57OW3MBrj6hXBFx/nNoC9MPMTcr5YA7NQbg==", - "license": "MIT", - "dependencies": { - "type-fest": "^0.7.1" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/stacktrace-parser/node_modules/type-fest": { - "version": "0.7.1", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.7.1.tgz", - "integrity": "sha512-Ne2YiiGN8bmrmJJEuTWTLJR32nh/JdL1+PSicowtNb0WFpn59GK8/lfD61bVtzguz7b3PBt74nxpv/Pw5po5Rg==", - "license": "(MIT OR CC0-1.0)", - "engines": { - "node": ">=8" - } - }, "node_modules/statuses": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", @@ -36180,7 +36044,6 @@ "@documenso/email": "*", "@documenso/prisma": "*", "@documenso/signing": "*", - "@honeybadger-io/js": "^6.10.1", "@lingui/core": "^5.2.0", "@lingui/macro": "^5.2.0", "@lingui/react": "^5.2.0", diff --git a/packages/api/v1/implementation.ts b/packages/api/v1/implementation.ts index 819dc64b9..53becfeda 100644 --- a/packages/api/v1/implementation.ts +++ b/packages/api/v1/implementation.ts @@ -77,9 +77,15 @@ export const ApiContractV1Implementation = tsr.router(ApiContractV1, { }; }), - getDocument: authenticatedMiddleware(async (args, user, team) => { + getDocument: authenticatedMiddleware(async (args, user, team, { logger }) => { const { id: documentId } = args.params; + logger.info({ + input: { + id: documentId, + }, + }); + try { const document = await getDocumentById({ documentId: Number(documentId), @@ -139,10 +145,16 @@ export const ApiContractV1Implementation = tsr.router(ApiContractV1, { } }), - downloadSignedDocument: authenticatedMiddleware(async (args, user, team) => { + downloadSignedDocument: authenticatedMiddleware(async (args, user, team, { logger }) => { const { id: documentId } = args.params; const { downloadOriginalDocument } = args.query; + logger.info({ + input: { + id: documentId, + }, + }); + try { if (process.env.NEXT_PUBLIC_UPLOAD_TRANSPORT !== 's3') { return { @@ -204,9 +216,15 @@ export const ApiContractV1Implementation = tsr.router(ApiContractV1, { } }), - deleteDocument: authenticatedMiddleware(async (args, user, team, { metadata }) => { + deleteDocument: authenticatedMiddleware(async (args, user, team, { logger, metadata }) => { const { id: documentId } = args.params; + logger.info({ + input: { + id: documentId, + }, + }); + try { const document = await getDocumentById({ documentId: Number(documentId), @@ -382,9 +400,15 @@ export const ApiContractV1Implementation = tsr.router(ApiContractV1, { } }), - deleteTemplate: authenticatedMiddleware(async (args, user, team) => { + deleteTemplate: authenticatedMiddleware(async (args, user, team, { logger }) => { const { id: templateId } = args.params; + logger.info({ + input: { + id: templateId, + }, + }); + try { const deletedTemplate = await deleteTemplate({ id: Number(templateId), @@ -406,9 +430,15 @@ export const ApiContractV1Implementation = tsr.router(ApiContractV1, { } }), - getTemplate: authenticatedMiddleware(async (args, user, team) => { + getTemplate: authenticatedMiddleware(async (args, user, team, { logger }) => { const { id: templateId } = args.params; + logger.info({ + input: { + id: templateId, + }, + }); + try { const template = await getTemplateById({ id: Number(templateId), @@ -463,202 +493,224 @@ export const ApiContractV1Implementation = tsr.router(ApiContractV1, { } }), - createDocumentFromTemplate: authenticatedMiddleware(async (args, user, team, { metadata }) => { - const { body, params } = args; + createDocumentFromTemplate: authenticatedMiddleware( + async (args, user, team, { logger, metadata }) => { + const { body, params } = args; - const { remaining } = await getServerLimits({ userId: user.id, teamId: team?.id }); - - if (remaining.documents <= 0) { - return { - status: 400, - body: { - message: 'You have reached the maximum number of documents allowed for this month', + logger.info({ + input: { + templateId: params.templateId, }, - }; - } - - const templateId = Number(params.templateId); - - const fileName = body.title.endsWith('.pdf') ? body.title : `${body.title}.pdf`; - - const document = await createDocumentFromTemplateLegacy({ - templateId, - userId: user.id, - teamId: team?.id, - recipients: body.recipients, - }); - - let documentDataId = document.documentDataId; - - if (body.formValues) { - const pdf = await getFileServerSide(document.documentData); - - const prefilled = await insertFormValuesInPdf({ - pdf: Buffer.from(pdf), - formValues: body.formValues, }); - const newDocumentData = await putPdfFileServerSide({ - name: fileName, - type: 'application/pdf', - arrayBuffer: async () => Promise.resolve(prefilled), - }); + const { remaining } = await getServerLimits({ userId: user.id, teamId: team?.id }); - documentDataId = newDocumentData.id; - } - - await updateDocument({ - documentId: document.id, - userId: user.id, - teamId: team?.id, - data: { - title: fileName, - externalId: body.externalId || null, - formValues: body.formValues, - documentData: { - connect: { - id: documentDataId, + if (remaining.documents <= 0) { + return { + status: 400, + body: { + message: 'You have reached the maximum number of documents allowed for this month', }, - }, - }, - }); + }; + } - if (body.meta) { - await upsertDocumentMeta({ - documentId: document.id, - userId: user.id, - teamId: team?.id, - ...body.meta, - requestMetadata: metadata, - }); - } + const templateId = Number(params.templateId); - if (body.authOptions) { - await updateDocumentSettings({ - documentId: document.id, - userId: user.id, - teamId: team?.id, - data: body.authOptions, - requestMetadata: metadata, - }); - } + const fileName = body.title.endsWith('.pdf') ? body.title : `${body.title}.pdf`; - return { - status: 200, - body: { - documentId: document.id, - recipients: document.recipients.map((recipient) => ({ - recipientId: recipient.id, - name: recipient.name, - email: recipient.email, - token: recipient.token, - role: recipient.role, - signingOrder: recipient.signingOrder, - - signingUrl: `${NEXT_PUBLIC_WEBAPP_URL()}/sign/${recipient.token}`, - })), - }, - }; - }), - - generateDocumentFromTemplate: authenticatedMiddleware(async (args, user, team, { metadata }) => { - const { body, params } = args; - - const { remaining } = await getServerLimits({ userId: user.id, teamId: team?.id }); - - if (remaining.documents <= 0) { - return { - status: 400, - body: { - message: 'You have reached the maximum number of documents allowed for this month', - }, - }; - } - - const templateId = Number(params.templateId); - - let document: Awaited> | null = null; - - try { - document = await createDocumentFromTemplate({ + const document = await createDocumentFromTemplateLegacy({ templateId, - externalId: body.externalId || null, userId: user.id, teamId: team?.id, recipients: body.recipients, - prefillFields: body.prefillFields, - override: { - title: body.title, - ...body.meta, - }, - requestMetadata: metadata, - }); - } catch (err) { - return AppError.toRestAPIError(err); - } - - if (body.formValues) { - const fileName = document.title.endsWith('.pdf') ? document.title : `${document.title}.pdf`; - - const pdf = await getFileServerSide(document.documentData); - - const prefilled = await insertFormValuesInPdf({ - pdf: Buffer.from(pdf), - formValues: body.formValues, }); - const newDocumentData = await putPdfFileServerSide({ - name: fileName, - type: 'application/pdf', - arrayBuffer: async () => Promise.resolve(prefilled), - }); + let documentDataId = document.documentDataId; + + if (body.formValues) { + const pdf = await getFileServerSide(document.documentData); + + const prefilled = await insertFormValuesInPdf({ + pdf: Buffer.from(pdf), + formValues: body.formValues, + }); + + const newDocumentData = await putPdfFileServerSide({ + name: fileName, + type: 'application/pdf', + arrayBuffer: async () => Promise.resolve(prefilled), + }); + + documentDataId = newDocumentData.id; + } await updateDocument({ documentId: document.id, userId: user.id, teamId: team?.id, data: { + title: fileName, + externalId: body.externalId || null, formValues: body.formValues, documentData: { connect: { - id: newDocumentData.id, + id: documentDataId, }, }, }, }); - } - if (body.authOptions) { - await updateDocumentSettings({ - documentId: document.id, - userId: user.id, - teamId: team?.id, - data: body.authOptions, - requestMetadata: metadata, + if (body.meta) { + await upsertDocumentMeta({ + documentId: document.id, + userId: user.id, + teamId: team?.id, + ...body.meta, + requestMetadata: metadata, + }); + } + + if (body.authOptions) { + await updateDocumentSettings({ + documentId: document.id, + userId: user.id, + teamId: team?.id, + data: body.authOptions, + requestMetadata: metadata, + }); + } + + return { + status: 200, + body: { + documentId: document.id, + recipients: document.recipients.map((recipient) => ({ + recipientId: recipient.id, + name: recipient.name, + email: recipient.email, + token: recipient.token, + role: recipient.role, + signingOrder: recipient.signingOrder, + + signingUrl: `${NEXT_PUBLIC_WEBAPP_URL()}/sign/${recipient.token}`, + })), + }, + }; + }, + ), + + generateDocumentFromTemplate: authenticatedMiddleware( + async (args, user, team, { logger, metadata }) => { + const { body, params } = args; + + logger.info({ + input: { + templateId: params.templateId, + }, }); - } - return { - status: 200, - body: { - documentId: document.id, - recipients: document.recipients.map((recipient) => ({ - recipientId: recipient.id, - name: recipient.name, - email: recipient.email, - token: recipient.token, - role: recipient.role, - signingOrder: recipient.signingOrder, - signingUrl: `${NEXT_PUBLIC_WEBAPP_URL()}/sign/${recipient.token}`, - })), - }, - }; - }), + const { remaining } = await getServerLimits({ userId: user.id, teamId: team?.id }); - sendDocument: authenticatedMiddleware(async (args, user, team, { metadata }) => { + if (remaining.documents <= 0) { + return { + status: 400, + body: { + message: 'You have reached the maximum number of documents allowed for this month', + }, + }; + } + + const templateId = Number(params.templateId); + + let document: Awaited> | null = null; + + try { + document = await createDocumentFromTemplate({ + templateId, + externalId: body.externalId || null, + userId: user.id, + teamId: team?.id, + recipients: body.recipients, + prefillFields: body.prefillFields, + override: { + title: body.title, + ...body.meta, + }, + requestMetadata: metadata, + }); + } catch (err) { + return AppError.toRestAPIError(err); + } + + if (body.formValues) { + const fileName = document.title.endsWith('.pdf') ? document.title : `${document.title}.pdf`; + + const pdf = await getFileServerSide(document.documentData); + + const prefilled = await insertFormValuesInPdf({ + pdf: Buffer.from(pdf), + formValues: body.formValues, + }); + + const newDocumentData = await putPdfFileServerSide({ + name: fileName, + type: 'application/pdf', + arrayBuffer: async () => Promise.resolve(prefilled), + }); + + await updateDocument({ + documentId: document.id, + userId: user.id, + teamId: team?.id, + data: { + formValues: body.formValues, + documentData: { + connect: { + id: newDocumentData.id, + }, + }, + }, + }); + } + + if (body.authOptions) { + await updateDocumentSettings({ + documentId: document.id, + userId: user.id, + teamId: team?.id, + data: body.authOptions, + requestMetadata: metadata, + }); + } + + return { + status: 200, + body: { + documentId: document.id, + recipients: document.recipients.map((recipient) => ({ + recipientId: recipient.id, + name: recipient.name, + email: recipient.email, + token: recipient.token, + role: recipient.role, + signingOrder: recipient.signingOrder, + signingUrl: `${NEXT_PUBLIC_WEBAPP_URL()}/sign/${recipient.token}`, + })), + }, + }; + }, + ), + + sendDocument: authenticatedMiddleware(async (args, user, team, { logger, metadata }) => { const { id: documentId } = args.params; const { sendEmail, sendCompletionEmails } = args.body; + logger.info({ + input: { + id: documentId, + }, + }); + try { const document = await getDocumentById({ documentId: Number(documentId), @@ -730,10 +782,16 @@ export const ApiContractV1Implementation = tsr.router(ApiContractV1, { } }), - resendDocument: authenticatedMiddleware(async (args, user, team, { metadata }) => { + resendDocument: authenticatedMiddleware(async (args, user, team, { logger, metadata }) => { const { id: documentId } = args.params; const { recipients } = args.body; + logger.info({ + input: { + id: documentId, + }, + }); + try { await resendDocument({ userId: user.id, @@ -759,10 +817,16 @@ export const ApiContractV1Implementation = tsr.router(ApiContractV1, { } }), - createRecipient: authenticatedMiddleware(async (args, user, team, { metadata }) => { + createRecipient: authenticatedMiddleware(async (args, user, team, { logger, metadata }) => { const { id: documentId } = args.params; const { name, email, role, authOptions, signingOrder } = args.body; + logger.info({ + input: { + id: documentId, + }, + }); + const document = await getDocumentById({ documentId: Number(documentId), userId: user.id, @@ -850,10 +914,17 @@ export const ApiContractV1Implementation = tsr.router(ApiContractV1, { } }), - updateRecipient: authenticatedMiddleware(async (args, user, team, { metadata }) => { + updateRecipient: authenticatedMiddleware(async (args, user, team, { logger, metadata }) => { const { id: documentId, recipientId } = args.params; const { name, email, role, authOptions, signingOrder } = args.body; + logger.info({ + input: { + id: documentId, + recipientId, + }, + }); + const document = await getDocumentById({ documentId: Number(documentId), userId: user.id, @@ -916,9 +987,16 @@ export const ApiContractV1Implementation = tsr.router(ApiContractV1, { }; }), - deleteRecipient: authenticatedMiddleware(async (args, user, team, { metadata }) => { + deleteRecipient: authenticatedMiddleware(async (args, user, team, { logger, metadata }) => { const { id: documentId, recipientId } = args.params; + logger.info({ + input: { + id: documentId, + recipientId, + }, + }); + const document = await getDocumentById({ documentId: Number(documentId), userId: user.id, @@ -970,8 +1048,15 @@ export const ApiContractV1Implementation = tsr.router(ApiContractV1, { }; }), - createField: authenticatedMiddleware(async (args, user, team, { metadata }) => { + createField: authenticatedMiddleware(async (args, user, team, { logger, metadata }) => { const { id: documentId } = args.params; + + logger.info({ + input: { + id: documentId, + }, + }); + const fields = Array.isArray(args.body) ? args.body : [args.body]; const document = await prisma.document.findFirst({ @@ -1131,11 +1216,18 @@ export const ApiContractV1Implementation = tsr.router(ApiContractV1, { } }), - updateField: authenticatedMiddleware(async (args, user, team, { metadata }) => { + updateField: authenticatedMiddleware(async (args, user, team, { logger, metadata }) => { const { id: documentId, fieldId } = args.params; const { recipientId, type, pageNumber, pageWidth, pageHeight, pageX, pageY, fieldMeta } = args.body; + logger.info({ + input: { + id: documentId, + fieldId, + }, + }); + const document = await getDocumentById({ documentId: Number(documentId), userId: user.id, @@ -1222,9 +1314,16 @@ export const ApiContractV1Implementation = tsr.router(ApiContractV1, { }; }), - deleteField: authenticatedMiddleware(async (args, user, team, { metadata }) => { + deleteField: authenticatedMiddleware(async (args, user, team, { logger, metadata }) => { const { id: documentId, fieldId } = args.params; + logger.info({ + input: { + id: documentId, + fieldId, + }, + }); + const document = await getDocumentById({ documentId: Number(documentId), userId: user.id, diff --git a/packages/api/v1/middleware/authenticated.ts b/packages/api/v1/middleware/authenticated.ts index 13366b42a..d83be7e8b 100644 --- a/packages/api/v1/middleware/authenticated.ts +++ b/packages/api/v1/middleware/authenticated.ts @@ -1,10 +1,13 @@ import type { Team, User } from '@prisma/client'; import type { TsRestRequest } from '@ts-rest/serverless'; +import type { Logger } from 'pino'; import { AppError, AppErrorCode } from '@documenso/lib/errors/app-error'; import { getApiTokenByToken } from '@documenso/lib/server-only/public-api/get-api-token-by-token'; +import type { BaseApiLog, RootApiLog } from '@documenso/lib/types/api-logs'; import type { ApiRequestMetadata } from '@documenso/lib/universal/extract-request-metadata'; import { extractRequestMetadata } from '@documenso/lib/universal/extract-request-metadata'; +import { nanoid } from '@documenso/lib/universal/id'; import { logger } from '@documenso/lib/utils/logger'; type B = { @@ -28,10 +31,24 @@ export const authenticatedMiddleware = < args: T & { req: TsRestRequest }, user: Pick, team: Team, - options: { metadata: ApiRequestMetadata }, + options: { metadata: ApiRequestMetadata; logger: Logger }, ) => Promise, ) => { return async (args: T, { request }: B) => { + const requestMetadata = extractRequestMetadata(request); + + const apiLogger = logger.child({ + ipAddress: requestMetadata.ipAddress, + userAgent: requestMetadata.userAgent, + requestId: nanoid(), + } satisfies RootApiLog); + + const infoToLog: BaseApiLog = { + auth: 'api', + source: 'apiV1', + path: request.url, + }; + try { const { authorization } = args.headers; @@ -52,8 +69,14 @@ export const authenticatedMiddleware = < }); } + apiLogger.info({ + ...infoToLog, + userId: apiToken.user.id, + apiTokenId: apiToken.id, + } satisfies BaseApiLog); + const metadata: ApiRequestMetadata = { - requestMetadata: extractRequestMetadata(request), + requestMetadata, source: 'apiV1', auth: 'api', auditUser: { @@ -63,17 +86,6 @@ export const authenticatedMiddleware = < }, }; - // Todo: Get from Hono context instead. - logger.info({ - ipAddress: metadata.requestMetadata.ipAddress, - userAgent: metadata.requestMetadata.userAgent, - auth: 'api', - source: 'apiV1', - path: request.url, - userId: apiToken.user.id, - apiTokenId: apiToken.id, - }); - return await handler( { ...args, @@ -81,10 +93,12 @@ export const authenticatedMiddleware = < }, apiToken.user, apiToken.team, - { metadata }, + { metadata, logger: apiLogger }, ); } catch (err) { - console.log({ err: err }); + console.log({ err }); + + apiLogger.info(infoToLog); let message = 'Unauthorized'; diff --git a/packages/lib/package.json b/packages/lib/package.json index 3a06087bd..5d2a4053e 100644 --- a/packages/lib/package.json +++ b/packages/lib/package.json @@ -23,7 +23,6 @@ "@documenso/email": "*", "@documenso/prisma": "*", "@documenso/signing": "*", - "@honeybadger-io/js": "^6.10.1", "@lingui/core": "^5.2.0", "@lingui/macro": "^5.2.0", "@lingui/react": "^5.2.0", diff --git a/packages/lib/types/api-logs.ts b/packages/lib/types/api-logs.ts new file mode 100644 index 000000000..0a7db4966 --- /dev/null +++ b/packages/lib/types/api-logs.ts @@ -0,0 +1,29 @@ +import type { ApiRequestMetadata } from '../universal/extract-request-metadata'; + +/** + * The minimum required fields that the parent API logger must contain. + */ +export type RootApiLog = { + ipAddress?: string; + userAgent?: string; + requestId: string; +}; + +/** + * The minimum API log that must be logged at the start of every API request. + */ +export type BaseApiLog = Partial & { + path: string; + auth: ApiRequestMetadata['auth']; + source: ApiRequestMetadata['source']; + userId?: number | null; + apiTokenId?: number | null; +}; + +/** + * The TRPC API log. + */ +export type TrpcApiLog = BaseApiLog & { + trpcMiddleware: string; + unverifiedTeamId?: number | null; +}; diff --git a/packages/lib/universal/extract-request-metadata.ts b/packages/lib/universal/extract-request-metadata.ts index 6e24dc74a..c0aab29be 100644 --- a/packages/lib/universal/extract-request-metadata.ts +++ b/packages/lib/universal/extract-request-metadata.ts @@ -1,5 +1,7 @@ import { z } from 'zod'; +import { getIpAddress } from './get-ip-address'; + const ZIpSchema = z.string().ip(); export const ZRequestMetadataSchema = z.object({ @@ -40,11 +42,13 @@ export type ApiRequestMetadata = { }; export const extractRequestMetadata = (req: Request): RequestMetadata => { - const forwardedFor = req.headers.get('x-forwarded-for'); - const ip = forwardedFor - ?.split(',') - .map((ip) => ip.trim()) - .at(0); + let ip: string | undefined = undefined; + + try { + ip = getIpAddress(req); + } catch { + // Do nothing. + } const parsedIp = ZIpSchema.safeParse(ip); diff --git a/packages/lib/utils/logger-legacy.ts b/packages/lib/utils/logger-legacy.ts deleted file mode 100644 index b708f2bdd..000000000 --- a/packages/lib/utils/logger-legacy.ts +++ /dev/null @@ -1,112 +0,0 @@ -import Honeybadger from '@honeybadger-io/js'; - -import { env } from './env'; - -export const buildLogger = () => { - if (env('NEXT_PRIVATE_LOGGER_HONEY_BADGER_API_KEY')) { - return new HoneybadgerLogger(); - } - - return new DefaultLogger(); -}; - -interface LoggerDescriptionOptions { - method?: string; - path?: string; - context?: Record; - - /** - * The type of log to be captured. - * - * Defaults to `info`. - */ - level?: 'info' | 'error' | 'critical'; -} - -/** - * Basic logger implementation intended to be used in the server side for capturing - * explicit errors and other logs. - * - * Not intended to capture the request and responses. - */ -interface Logger { - log(message: string, options?: LoggerDescriptionOptions): void; - - error(error: Error, options?: LoggerDescriptionOptions): void; -} - -class DefaultLogger implements Logger { - log(_message: string, _options?: LoggerDescriptionOptions) { - // Do nothing. - } - - error(_error: Error, _options?: LoggerDescriptionOptions): void { - // Do nothing. - } -} - -class HoneybadgerLogger implements Logger { - constructor() { - if (!env('NEXT_PRIVATE_LOGGER_HONEY_BADGER_API_KEY')) { - throw new Error('NEXT_PRIVATE_LOGGER_HONEY_BADGER_API_KEY is not set'); - } - - Honeybadger.configure({ - apiKey: env('NEXT_PRIVATE_LOGGER_HONEY_BADGER_API_KEY'), - }); - } - - /** - * Honeybadger doesn't really have a non-error logging system. - */ - log(message: string, options?: LoggerDescriptionOptions) { - const { context = {}, level = 'info' } = options || {}; - - try { - Honeybadger.event({ - message, - context: { - level, - ...context, - }, - }); - } catch (err) { - console.error(err); - - // Do nothing. - } - } - - error(error: Error, options?: LoggerDescriptionOptions): void { - const { context = {}, level = 'error', method, path } = options || {}; - - // const tags = [`level:${level}`]; - const tags = []; - - let errorMessage = error.message; - - if (method) { - tags.push(`method:${method}`); - - errorMessage = `[${method}]: ${error.message}`; - } - - if (path) { - tags.push(`path:${path}`); - } - - try { - Honeybadger.notify(errorMessage, { - context: { - level, - ...context, - }, - tags, - }); - } catch (err) { - console.error(err); - - // Do nothing. - } - } -} diff --git a/packages/lib/utils/logger.ts b/packages/lib/utils/logger.ts index e60ab0142..8a76f270b 100644 --- a/packages/lib/utils/logger.ts +++ b/packages/lib/utils/logger.ts @@ -1,27 +1,35 @@ -import { pino } from 'pino'; +import { type TransportTargetOptions, pino } from 'pino'; -// const transports: TransportTargetOptions[] = []; +import { env } from './env'; -// if (env('NEXT_PRIVATE_LOGGING_DEV')) { -// transports.push({ -// target: 'pino-pretty', -// level: 'info', -// }); -// } +const transports: TransportTargetOptions[] = []; -// const loggingFilePath = env('NEXT_PRIVATE_LOGGING_FILE_PATH'); +if (env('NODE_ENV') !== 'production' && !env('INTERNAL_FORCE_JSON_LOGGER')) { + transports.push({ + target: 'pino-pretty', + level: 'info', + }); +} -// if (loggingFilePath) { -// transports.push({ -// target: 'pino/file', -// level: 'info', -// options: { -// destination: loggingFilePath, -// mkdir: true, -// }, -// }); -// } +const loggingFilePath = env('NEXT_PRIVATE_LOGGER_FILE_PATH'); + +if (loggingFilePath) { + transports.push({ + target: 'pino/file', + level: 'info', + options: { + destination: loggingFilePath, + mkdir: true, + }, + }); +} export const logger = pino({ level: 'info', + transport: + transports.length > 0 + ? { + targets: transports, + } + : undefined, }); diff --git a/packages/trpc/server/admin-router/create-admin-organisation.ts b/packages/trpc/server/admin-router/create-admin-organisation.ts index 1b7c4660d..e20df8854 100644 --- a/packages/trpc/server/admin-router/create-admin-organisation.ts +++ b/packages/trpc/server/admin-router/create-admin-organisation.ts @@ -12,9 +12,15 @@ import { export const createAdminOrganisationRoute = adminProcedure .input(ZCreateAdminOrganisationRequestSchema) .output(ZCreateAdminOrganisationResponseSchema) - .mutation(async ({ input }) => { + .mutation(async ({ input, ctx }) => { const { ownerUserId, data } = input; + ctx.logger.info({ + input: { + ownerUserId, + }, + }); + const organisation = await createOrganisation({ userId: ownerUserId, name: data.name, diff --git a/packages/trpc/server/admin-router/create-stripe-customer.ts b/packages/trpc/server/admin-router/create-stripe-customer.ts index 7e68e6eaa..fc846e730 100644 --- a/packages/trpc/server/admin-router/create-stripe-customer.ts +++ b/packages/trpc/server/admin-router/create-stripe-customer.ts @@ -11,9 +11,15 @@ import { export const createStripeCustomerRoute = adminProcedure .input(ZCreateStripeCustomerRequestSchema) .output(ZCreateStripeCustomerResponseSchema) - .mutation(async ({ input }) => { + .mutation(async ({ input, ctx }) => { const { organisationId } = input; + ctx.logger.info({ + input: { + organisationId, + }, + }); + const organisation = await prisma.organisation.findUnique({ where: { id: organisationId, diff --git a/packages/trpc/server/admin-router/create-subscription-claim.ts b/packages/trpc/server/admin-router/create-subscription-claim.ts index 92fcbe0f5..b7b4a21e2 100644 --- a/packages/trpc/server/admin-router/create-subscription-claim.ts +++ b/packages/trpc/server/admin-router/create-subscription-claim.ts @@ -9,9 +9,13 @@ import { export const createSubscriptionClaimRoute = adminProcedure .input(ZCreateSubscriptionClaimRequestSchema) .output(ZCreateSubscriptionClaimResponseSchema) - .mutation(async ({ input }) => { + .mutation(async ({ input, ctx }) => { const { name, teamCount, memberCount, flags } = input; + ctx.logger.info({ + input, + }); + await prisma.subscriptionClaim.create({ data: { name, diff --git a/packages/trpc/server/admin-router/delete-subscription-claim.ts b/packages/trpc/server/admin-router/delete-subscription-claim.ts index 41a3b93a8..183907087 100644 --- a/packages/trpc/server/admin-router/delete-subscription-claim.ts +++ b/packages/trpc/server/admin-router/delete-subscription-claim.ts @@ -10,9 +10,15 @@ import { export const deleteSubscriptionClaimRoute = adminProcedure .input(ZDeleteSubscriptionClaimRequestSchema) .output(ZDeleteSubscriptionClaimResponseSchema) - .mutation(async ({ input }) => { + .mutation(async ({ input, ctx }) => { const { id } = input; + ctx.logger.info({ + input: { + id, + }, + }); + const existingClaim = await prisma.subscriptionClaim.findFirst({ where: { id, diff --git a/packages/trpc/server/admin-router/get-admin-organisation.ts b/packages/trpc/server/admin-router/get-admin-organisation.ts index d7ef4fd23..8990e0521 100644 --- a/packages/trpc/server/admin-router/get-admin-organisation.ts +++ b/packages/trpc/server/admin-router/get-admin-organisation.ts @@ -10,9 +10,15 @@ import { export const getAdminOrganisationRoute = adminProcedure .input(ZGetAdminOrganisationRequestSchema) .output(ZGetAdminOrganisationResponseSchema) - .query(async ({ input }) => { + .query(async ({ input, ctx }) => { const { organisationId } = input; + ctx.logger.info({ + input: { + organisationId, + }, + }); + return await getAdminOrganisation({ organisationId, }); diff --git a/packages/trpc/server/admin-router/router.ts b/packages/trpc/server/admin-router/router.ts index 08b49b38e..aca1a03d6 100644 --- a/packages/trpc/server/admin-router/router.ts +++ b/packages/trpc/server/admin-router/router.ts @@ -61,17 +61,30 @@ export const adminRouter = router({ updateUser: adminProcedure .input(ZAdminUpdateProfileMutationSchema) - .mutation(async ({ input }) => { + .mutation(async ({ input, ctx }) => { const { id, name, email, roles } = input; + ctx.logger.info({ + input: { + id, + roles, + }, + }); + return await updateUser({ id, name, email, roles }); }), updateRecipient: adminProcedure .input(ZAdminUpdateRecipientMutationSchema) - .mutation(async ({ input }) => { + .mutation(async ({ input, ctx }) => { const { id, name, email } = input; + ctx.logger.info({ + input: { + id, + }, + }); + return await updateRecipient({ id, name, email }); }), @@ -80,6 +93,12 @@ export const adminRouter = router({ .mutation(async ({ ctx, input }) => { const { id, enabled, data } = input; + ctx.logger.info({ + input: { + id, + }, + }); + return await upsertSiteSetting({ id, enabled, @@ -90,9 +109,15 @@ export const adminRouter = router({ resealDocument: adminProcedure .input(ZAdminResealDocumentMutationSchema) - .mutation(async ({ input }) => { + .mutation(async ({ input, ctx }) => { const { id } = input; + ctx.logger.info({ + input: { + id, + }, + }); + const document = await getEntireDocument({ id }); const isResealing = isDocumentCompleted(document.status); @@ -100,44 +125,75 @@ export const adminRouter = router({ return await sealDocument({ documentId: id, isResealing }); }), - enableUser: adminProcedure.input(ZAdminEnableUserMutationSchema).mutation(async ({ input }) => { - const { id } = input; + enableUser: adminProcedure + .input(ZAdminEnableUserMutationSchema) + .mutation(async ({ input, ctx }) => { + const { id } = input; - const user = await getUserById({ id }).catch(() => null); - - if (!user) { - throw new AppError(AppErrorCode.NOT_FOUND, { - message: 'User not found', + ctx.logger.info({ + input: { + id, + }, }); - } - return await enableUser({ id }); - }), + const user = await getUserById({ id }).catch(() => null); - disableUser: adminProcedure.input(ZAdminDisableUserMutationSchema).mutation(async ({ input }) => { - const { id } = input; + if (!user) { + throw new AppError(AppErrorCode.NOT_FOUND, { + message: 'User not found', + }); + } - const user = await getUserById({ id }).catch(() => null); + return await enableUser({ id }); + }), - if (!user) { - throw new AppError(AppErrorCode.NOT_FOUND, { - message: 'User not found', + disableUser: adminProcedure + .input(ZAdminDisableUserMutationSchema) + .mutation(async ({ input, ctx }) => { + const { id } = input; + + ctx.logger.info({ + input: { + id, + }, }); - } - return await disableUser({ id }); - }), + const user = await getUserById({ id }).catch(() => null); - deleteUser: adminProcedure.input(ZAdminDeleteUserMutationSchema).mutation(async ({ input }) => { - const { id } = input; + if (!user) { + throw new AppError(AppErrorCode.NOT_FOUND, { + message: 'User not found', + }); + } - return await deleteUser({ id }); - }), + return await disableUser({ id }); + }), + + deleteUser: adminProcedure + .input(ZAdminDeleteUserMutationSchema) + .mutation(async ({ input, ctx }) => { + const { id } = input; + + ctx.logger.info({ + input: { + id, + }, + }); + + return await deleteUser({ id }); + }), deleteDocument: adminProcedure .input(ZAdminDeleteDocumentMutationSchema) .mutation(async ({ ctx, input }) => { const { id, reason } = input; + + ctx.logger.info({ + input: { + id, + }, + }); + await sendDeleteEmail({ documentId: id, reason }); return await superDeleteDocument({ diff --git a/packages/trpc/server/admin-router/update-admin-organisation.ts b/packages/trpc/server/admin-router/update-admin-organisation.ts index 25665a97b..6b4211939 100644 --- a/packages/trpc/server/admin-router/update-admin-organisation.ts +++ b/packages/trpc/server/admin-router/update-admin-organisation.ts @@ -10,9 +10,15 @@ import { export const updateAdminOrganisationRoute = adminProcedure .input(ZUpdateAdminOrganisationRequestSchema) .output(ZUpdateAdminOrganisationResponseSchema) - .mutation(async ({ input }) => { + .mutation(async ({ input, ctx }) => { const { organisationId, data } = input; + ctx.logger.info({ + input: { + organisationId, + }, + }); + const organisation = await prisma.organisation.findUnique({ where: { id: organisationId, diff --git a/packages/trpc/server/admin-router/update-subscription-claim.ts b/packages/trpc/server/admin-router/update-subscription-claim.ts index e3c1072f6..f5ac9f984 100644 --- a/packages/trpc/server/admin-router/update-subscription-claim.ts +++ b/packages/trpc/server/admin-router/update-subscription-claim.ts @@ -12,9 +12,13 @@ import { export const updateSubscriptionClaimRoute = adminProcedure .input(ZUpdateSubscriptionClaimRequestSchema) .output(ZUpdateSubscriptionClaimResponseSchema) - .mutation(async ({ input }) => { + .mutation(async ({ input, ctx }) => { const { id, data } = input; + ctx.logger.info({ + input, + }); + const existingClaim = await prisma.subscriptionClaim.findUnique({ where: { id }, }); diff --git a/packages/trpc/server/api-token-router/router.ts b/packages/trpc/server/api-token-router/router.ts index 778c07b9a..f1439060c 100644 --- a/packages/trpc/server/api-token-router/router.ts +++ b/packages/trpc/server/api-token-router/router.ts @@ -15,6 +15,12 @@ export const apiTokenRouter = router({ .mutation(async ({ input, ctx }) => { const { tokenName, teamId, expirationDate } = input; + ctx.logger.info({ + input: { + teamId, + }, + }); + return await createApiToken({ userId: ctx.user.id, teamId, @@ -28,6 +34,13 @@ export const apiTokenRouter = router({ .mutation(async ({ input, ctx }) => { const { id, teamId } = input; + ctx.logger.info({ + input: { + id, + teamId, + }, + }); + return await deleteTokenById({ id, teamId, diff --git a/packages/trpc/server/auth-router/router.ts b/packages/trpc/server/auth-router/router.ts index b35fd160e..2eb54d1e6 100644 --- a/packages/trpc/server/auth-router/router.ts +++ b/packages/trpc/server/auth-router/router.ts @@ -66,6 +66,12 @@ export const authRouter = router({ .mutation(async ({ ctx, input }) => { const { passkeyId } = input; + ctx.logger.info({ + input: { + passkeyId, + }, + }); + await deletePasskey({ userId: ctx.user.id, passkeyId, @@ -91,6 +97,12 @@ export const authRouter = router({ .mutation(async ({ ctx, input }) => { const { passkeyId, name } = input; + ctx.logger.info({ + input: { + passkeyId, + }, + }); + await updatePasskey({ userId: ctx.user.id, passkeyId, diff --git a/packages/trpc/server/billing/create-subscription.ts b/packages/trpc/server/billing/create-subscription.ts index 8a0be6825..6de407902 100644 --- a/packages/trpc/server/billing/create-subscription.ts +++ b/packages/trpc/server/billing/create-subscription.ts @@ -14,6 +14,13 @@ export const createSubscriptionRoute = authenticatedProcedure .mutation(async ({ ctx, input }) => { const { organisationId, priceId } = input; + ctx.logger.info({ + input: { + organisationId, + priceId, + }, + }); + const userId = ctx.user.id; if (!IS_BILLING_ENABLED()) { diff --git a/packages/trpc/server/billing/get-invoices.ts b/packages/trpc/server/billing/get-invoices.ts index bc25a837c..7693d7559 100644 --- a/packages/trpc/server/billing/get-invoices.ts +++ b/packages/trpc/server/billing/get-invoices.ts @@ -13,6 +13,12 @@ export const getInvoicesRoute = authenticatedProcedure .query(async ({ ctx, input }) => { const { organisationId } = input; + ctx.logger.info({ + input: { + organisationId, + }, + }); + const userId = ctx.user.id; if (!IS_BILLING_ENABLED()) { diff --git a/packages/trpc/server/billing/get-subscription.ts b/packages/trpc/server/billing/get-subscription.ts index 6c24dd455..9c7ffb4ea 100644 --- a/packages/trpc/server/billing/get-subscription.ts +++ b/packages/trpc/server/billing/get-subscription.ts @@ -11,6 +11,12 @@ export const getSubscriptionRoute = authenticatedProcedure .query(async ({ ctx, input }) => { const { organisationId } = input; + ctx.logger.info({ + input: { + organisationId, + }, + }); + const userId = ctx.user.id; if (!IS_BILLING_ENABLED()) { diff --git a/packages/trpc/server/billing/manage-subscription.ts b/packages/trpc/server/billing/manage-subscription.ts index 2d215067a..de621ad06 100644 --- a/packages/trpc/server/billing/manage-subscription.ts +++ b/packages/trpc/server/billing/manage-subscription.ts @@ -14,6 +14,12 @@ export const manageSubscriptionRoute = authenticatedProcedure .mutation(async ({ ctx, input }) => { const { organisationId } = input; + ctx.logger.info({ + input: { + organisationId, + }, + }); + const userId = ctx.user.id; if (!IS_BILLING_ENABLED()) { diff --git a/packages/trpc/server/context.ts b/packages/trpc/server/context.ts index a11cab223..ae319c4ce 100644 --- a/packages/trpc/server/context.ts +++ b/packages/trpc/server/context.ts @@ -5,8 +5,10 @@ import { z } from 'zod'; import type { SessionUser } from '@documenso/auth/server/lib/session/session'; import { getOptionalSession } from '@documenso/auth/server/lib/utils/get-session'; +import type { RootApiLog } from '@documenso/lib/types/api-logs'; import type { ApiRequestMetadata } from '@documenso/lib/universal/extract-request-metadata'; -import { extractRequestMetadata } from '@documenso/lib/universal/extract-request-metadata'; +import { alphaid } from '@documenso/lib/universal/id'; +import { logger } from '@documenso/lib/utils/logger'; // This is a bit nasty. Todo: Extract import type { HonoEnv } from '@documenso/remix/server/router'; @@ -22,16 +24,23 @@ export const createTrpcContext = async ({ const { session, user } = await getOptionalSession(c); const req = c.req.raw; - const logger = c.get('logger'); + + const requestMetadata = c.get('context').requestMetadata; const metadata: ApiRequestMetadata = { - requestMetadata: extractRequestMetadata(req), + requestMetadata, source: requestSource, auth: null, }; const rawTeamId = req.headers.get('x-team-id') || undefined; + const trpcLogger = logger.child({ + ipAddress: requestMetadata.ipAddress, + userAgent: requestMetadata.userAgent, + requestId: alphaid(), + } satisfies RootApiLog); + const teamId = z.coerce .number() .optional() @@ -40,7 +49,7 @@ export const createTrpcContext = async ({ if (!session || !user) { return { - logger, + logger: trpcLogger, session: null, user: null, teamId, @@ -50,7 +59,7 @@ export const createTrpcContext = async ({ } return { - logger, + logger: trpcLogger, session, user, teamId, diff --git a/packages/trpc/server/document-router/router.ts b/packages/trpc/server/document-router/router.ts index a2fa0467d..08cf6cd09 100644 --- a/packages/trpc/server/document-router/router.ts +++ b/packages/trpc/server/document-router/router.ts @@ -62,6 +62,7 @@ export const documentRouter = router({ find: findInboxRoute, getCount: getInboxCountRoute, }, + updateDocument: updateDocumentRoute, /** * @private @@ -72,6 +73,12 @@ export const documentRouter = router({ const { teamId } = ctx; const { documentId } = input; + ctx.logger.info({ + input: { + documentId, + }, + }); + return await getDocumentById({ userId: ctx.user.id, teamId, @@ -231,6 +238,13 @@ export const documentRouter = router({ const { teamId, user } = ctx; const { documentId, folderId } = input; + ctx.logger.info({ + input: { + documentId, + folderId, + }, + }); + return await getDocumentWithDetailsById({ userId: user.id, teamId, @@ -332,6 +346,12 @@ export const documentRouter = router({ const { user, teamId } = ctx; const { title, documentDataId, timezone, folderId } = input; + ctx.logger.info({ + input: { + folderId, + }, + }); + const { remaining } = await getServerLimits({ userId: user.id, teamId }); if (remaining.documents <= 0) { @@ -353,8 +373,6 @@ export const documentRouter = router({ }); }), - updateDocument: updateDocumentRoute, - /** * @public */ @@ -373,6 +391,12 @@ export const documentRouter = router({ const { teamId } = ctx; const { documentId } = input; + ctx.logger.info({ + input: { + documentId, + }, + }); + const userId = ctx.user.id; await deleteDocument({ @@ -396,6 +420,13 @@ export const documentRouter = router({ const { teamId } = ctx; const { documentId, signingOrder } = input; + ctx.logger.info({ + input: { + documentId, + signingOrder, + }, + }); + return await upsertDocumentMeta({ userId: ctx.user.id, teamId, @@ -427,6 +458,12 @@ export const documentRouter = router({ const { teamId } = ctx; const { documentId, meta = {} } = input; + ctx.logger.info({ + input: { + documentId, + }, + }); + if (Object.values(meta).length > 0) { await upsertDocumentMeta({ userId: ctx.user.id, @@ -474,6 +511,13 @@ export const documentRouter = router({ const { teamId } = ctx; const { documentId, recipients } = input; + ctx.logger.info({ + input: { + documentId, + recipients, + }, + }); + await resendDocument({ userId: ctx.user.id, teamId, @@ -503,6 +547,12 @@ export const documentRouter = router({ const { teamId, user } = ctx; const { documentId } = input; + ctx.logger.info({ + input: { + documentId, + }, + }); + return await duplicateDocument({ userId: user.id, teamId, @@ -544,6 +594,12 @@ export const documentRouter = router({ orderByDirection, } = input; + ctx.logger.info({ + input: { + documentId, + }, + }); + return await findDocumentAuditLogs({ userId: ctx.user.id, teamId, @@ -565,6 +621,12 @@ export const documentRouter = router({ const { teamId } = ctx; const { documentId } = input; + ctx.logger.info({ + input: { + documentId, + }, + }); + const document = await getDocumentById({ documentId, userId: ctx.user.id, @@ -597,6 +659,12 @@ export const documentRouter = router({ const { teamId } = ctx; const { documentId } = input; + ctx.logger.info({ + input: { + documentId, + }, + }); + const document = await getDocumentById({ documentId, userId: ctx.user.id, diff --git a/packages/trpc/server/document-router/update-document.ts b/packages/trpc/server/document-router/update-document.ts index ed2ca6acd..b60a592bb 100644 --- a/packages/trpc/server/document-router/update-document.ts +++ b/packages/trpc/server/document-router/update-document.ts @@ -19,6 +19,12 @@ export const updateDocumentRoute = authenticatedProcedure const { teamId } = ctx; const { documentId, data, meta = {} } = input; + ctx.logger.info({ + input: { + documentId, + }, + }); + const userId = ctx.user.id; if (Object.values(meta).length > 0) { diff --git a/packages/trpc/server/embedding-router/update-embedding-document.ts b/packages/trpc/server/embedding-router/update-embedding-document.ts index 9060b09ec..cd8dc305c 100644 --- a/packages/trpc/server/embedding-router/update-embedding-document.ts +++ b/packages/trpc/server/embedding-router/update-embedding-document.ts @@ -16,6 +16,12 @@ export const updateEmbeddingDocumentRoute = procedure .input(ZUpdateEmbeddingDocumentRequestSchema) .output(ZUpdateEmbeddingDocumentResponseSchema) .mutation(async ({ input, ctx }) => { + ctx.logger.info({ + input: { + documentId: input.documentId, + }, + }); + try { const authorizationHeader = ctx.req.headers.get('authorization'); diff --git a/packages/trpc/server/embedding-router/update-embedding-template.ts b/packages/trpc/server/embedding-router/update-embedding-template.ts index 52dca3004..38bf2a27a 100644 --- a/packages/trpc/server/embedding-router/update-embedding-template.ts +++ b/packages/trpc/server/embedding-router/update-embedding-template.ts @@ -15,6 +15,12 @@ export const updateEmbeddingTemplateRoute = procedure .input(ZUpdateEmbeddingTemplateRequestSchema) .output(ZUpdateEmbeddingTemplateResponseSchema) .mutation(async ({ input, ctx }) => { + ctx.logger.info({ + input: { + templateId: input.templateId, + }, + }); + try { const authorizationHeader = ctx.req.headers.get('authorization'); diff --git a/packages/trpc/server/field-router/router.ts b/packages/trpc/server/field-router/router.ts index 236afc360..1e022749c 100644 --- a/packages/trpc/server/field-router/router.ts +++ b/packages/trpc/server/field-router/router.ts @@ -62,6 +62,12 @@ export const fieldRouter = router({ const { teamId } = ctx; const { fieldId } = input; + ctx.logger.info({ + input: { + fieldId, + }, + }); + return await getFieldById({ userId: ctx.user.id, teamId, @@ -88,6 +94,12 @@ export const fieldRouter = router({ const { teamId } = ctx; const { documentId, field } = input; + ctx.logger.info({ + input: { + documentId, + }, + }); + const createdFields = await createDocumentFields({ userId: ctx.user.id, teamId, @@ -118,6 +130,12 @@ export const fieldRouter = router({ const { teamId } = ctx; const { documentId, fields } = input; + ctx.logger.info({ + input: { + documentId, + }, + }); + return await createDocumentFields({ userId: ctx.user.id, teamId, @@ -146,6 +164,12 @@ export const fieldRouter = router({ const { teamId } = ctx; const { documentId, field } = input; + ctx.logger.info({ + input: { + documentId, + }, + }); + const updatedFields = await updateDocumentFields({ userId: ctx.user.id, teamId, @@ -176,6 +200,12 @@ export const fieldRouter = router({ const { teamId } = ctx; const { documentId, fields } = input; + ctx.logger.info({ + input: { + documentId, + }, + }); + return await updateDocumentFields({ userId: ctx.user.id, teamId, @@ -203,6 +233,12 @@ export const fieldRouter = router({ const { teamId } = ctx; const { fieldId } = input; + ctx.logger.info({ + input: { + fieldId, + }, + }); + await deleteDocumentField({ userId: ctx.user.id, teamId, @@ -225,6 +261,12 @@ export const fieldRouter = router({ const { teamId } = ctx; const { documentId, fields } = input; + ctx.logger.info({ + input: { + documentId, + }, + }); + return await setFieldsForDocument({ documentId, userId: ctx.user.id, @@ -263,6 +305,12 @@ export const fieldRouter = router({ const { teamId } = ctx; const { templateId, field } = input; + ctx.logger.info({ + input: { + templateId, + }, + }); + const createdFields = await createTemplateFields({ userId: ctx.user.id, teamId, @@ -293,6 +341,12 @@ export const fieldRouter = router({ const { teamId } = ctx; const { fieldId } = input; + ctx.logger.info({ + input: { + fieldId, + }, + }); + return await getFieldById({ userId: ctx.user.id, teamId, @@ -319,6 +373,12 @@ export const fieldRouter = router({ const { teamId } = ctx; const { templateId, fields } = input; + ctx.logger.info({ + input: { + templateId, + }, + }); + return await createTemplateFields({ userId: ctx.user.id, teamId, @@ -346,6 +406,12 @@ export const fieldRouter = router({ const { teamId } = ctx; const { templateId, field } = input; + ctx.logger.info({ + input: { + templateId, + }, + }); + const updatedFields = await updateTemplateFields({ userId: ctx.user.id, teamId, @@ -375,6 +441,12 @@ export const fieldRouter = router({ const { teamId } = ctx; const { templateId, fields } = input; + ctx.logger.info({ + input: { + templateId, + }, + }); + return await updateTemplateFields({ userId: ctx.user.id, teamId, @@ -401,6 +473,12 @@ export const fieldRouter = router({ const { teamId } = ctx; const { fieldId } = input; + ctx.logger.info({ + input: { + fieldId, + }, + }); + await deleteTemplateField({ userId: ctx.user.id, teamId, @@ -422,6 +500,12 @@ export const fieldRouter = router({ const { teamId } = ctx; const { templateId, fields } = input; + ctx.logger.info({ + input: { + templateId, + }, + }); + return await setFieldsForTemplate({ templateId, userId: ctx.user.id, @@ -448,6 +532,12 @@ export const fieldRouter = router({ .mutation(async ({ input, ctx }) => { const { token, fieldId, value, isBase64, authOptions } = input; + ctx.logger.info({ + input: { + fieldId, + }, + }); + return await signFieldWithToken({ token, fieldId, @@ -467,6 +557,12 @@ export const fieldRouter = router({ .mutation(async ({ input, ctx }) => { const { token, fieldId } = input; + ctx.logger.info({ + input: { + fieldId, + }, + }); + return await removeSignedFieldWithToken({ token, fieldId, diff --git a/packages/trpc/server/folder-router/router.ts b/packages/trpc/server/folder-router/router.ts index fb3883481..f27998c84 100644 --- a/packages/trpc/server/folder-router/router.ts +++ b/packages/trpc/server/folder-router/router.ts @@ -42,6 +42,13 @@ export const folderRouter = router({ const { teamId, user } = ctx; const { parentId, type } = input; + ctx.logger.info({ + input: { + parentId, + type, + }, + }); + const folders = await findFolders({ userId: user.id, teamId, @@ -75,6 +82,13 @@ export const folderRouter = router({ const { teamId, user } = ctx; const { parentId, type } = input; + ctx.logger.info({ + input: { + parentId, + type, + }, + }); + const folders = await findFolders({ userId: user.id, teamId, @@ -107,6 +121,13 @@ export const folderRouter = router({ const { teamId, user } = ctx; const { name, parentId, type } = input; + ctx.logger.info({ + input: { + parentId, + type, + }, + }); + if (parentId) { try { await getFolderById({ @@ -146,6 +167,12 @@ export const folderRouter = router({ const { teamId, user } = ctx; const { id, name, visibility } = input; + ctx.logger.info({ + input: { + id, + }, + }); + const currentFolder = await getFolderById({ userId: user.id, teamId, @@ -177,6 +204,12 @@ export const folderRouter = router({ const { teamId, user } = ctx; const { id } = input; + ctx.logger.info({ + input: { + id, + }, + }); + await deleteFolder({ userId: user.id, teamId, @@ -193,6 +226,13 @@ export const folderRouter = router({ const { teamId, user } = ctx; const { id, parentId } = input; + ctx.logger.info({ + input: { + id, + parentId, + }, + }); + const currentFolder = await getFolderById({ userId: user.id, teamId, @@ -238,6 +278,13 @@ export const folderRouter = router({ const { teamId, user } = ctx; const { documentId, folderId } = input; + ctx.logger.info({ + input: { + documentId, + folderId, + }, + }); + if (folderId !== null) { try { await getFolderById({ @@ -277,6 +324,13 @@ export const folderRouter = router({ const { teamId, user } = ctx; const { templateId, folderId } = input; + ctx.logger.info({ + input: { + templateId, + folderId, + }, + }); + if (folderId !== null) { try { await getFolderById({ @@ -310,16 +364,24 @@ export const folderRouter = router({ * @private */ pinFolder: authenticatedProcedure.input(ZPinFolderSchema).mutation(async ({ ctx, input }) => { + const { folderId } = input; + + ctx.logger.info({ + input: { + folderId, + }, + }); + const currentFolder = await getFolderById({ userId: ctx.user.id, teamId: ctx.teamId, - folderId: input.folderId, + folderId, }); const result = await pinFolder({ userId: ctx.user.id, teamId: ctx.teamId, - folderId: input.folderId, + folderId, type: currentFolder.type, }); @@ -333,16 +395,24 @@ export const folderRouter = router({ * @private */ unpinFolder: authenticatedProcedure.input(ZUnpinFolderSchema).mutation(async ({ ctx, input }) => { + const { folderId } = input; + + ctx.logger.info({ + input: { + folderId, + }, + }); + const currentFolder = await getFolderById({ userId: ctx.user.id, teamId: ctx.teamId, - folderId: input.folderId, + folderId, }); const result = await unpinFolder({ userId: ctx.user.id, teamId: ctx.teamId, - folderId: input.folderId, + folderId, type: currentFolder.type, }); diff --git a/packages/trpc/server/organisation-router/create-organisation-group.ts b/packages/trpc/server/organisation-router/create-organisation-group.ts index 9f148764d..b2dc3cde9 100644 --- a/packages/trpc/server/organisation-router/create-organisation-group.ts +++ b/packages/trpc/server/organisation-router/create-organisation-group.ts @@ -24,6 +24,12 @@ export const createOrganisationGroupRoute = authenticatedProcedure const { organisationId, organisationRole, name, memberIds } = input; const { user } = ctx; + ctx.logger.info({ + input: { + organisationId, + }, + }); + const organisation = await prisma.organisation.findFirst({ where: buildOrganisationWhereQuery({ organisationId, diff --git a/packages/trpc/server/organisation-router/create-organisation-member-invites.ts b/packages/trpc/server/organisation-router/create-organisation-member-invites.ts index 0178570e0..e468ce6fa 100644 --- a/packages/trpc/server/organisation-router/create-organisation-member-invites.ts +++ b/packages/trpc/server/organisation-router/create-organisation-member-invites.ts @@ -14,6 +14,12 @@ export const createOrganisationMemberInvitesRoute = authenticatedProcedure const userId = ctx.user.id; const userName = ctx.user.name || ''; + ctx.logger.info({ + input: { + organisationId, + }, + }); + await createOrganisationMemberInvites({ userId, userName, diff --git a/packages/trpc/server/organisation-router/create-organisation.ts b/packages/trpc/server/organisation-router/create-organisation.ts index 39086b55e..c4c02f9df 100644 --- a/packages/trpc/server/organisation-router/create-organisation.ts +++ b/packages/trpc/server/organisation-router/create-organisation.ts @@ -23,6 +23,12 @@ export const createOrganisationRoute = authenticatedProcedure const { name, priceId } = input; const { user } = ctx; + ctx.logger.info({ + input: { + priceId, + }, + }); + // Check if user can create a free organiastion. if (IS_BILLING_ENABLED() && !priceId) { const userOrganisations = await prisma.organisation.findMany({ diff --git a/packages/trpc/server/organisation-router/delete-organisation-group.ts b/packages/trpc/server/organisation-router/delete-organisation-group.ts index cf8d0bae9..3b5aadbff 100644 --- a/packages/trpc/server/organisation-router/delete-organisation-group.ts +++ b/packages/trpc/server/organisation-router/delete-organisation-group.ts @@ -19,6 +19,13 @@ export const deleteOrganisationGroupRoute = authenticatedProcedure const { groupId, organisationId } = input; const { user } = ctx; + ctx.logger.info({ + input: { + groupId, + organisationId, + }, + }); + const organisation = await prisma.organisation.findFirst({ where: buildOrganisationWhereQuery({ organisationId, diff --git a/packages/trpc/server/organisation-router/delete-organisation-member-invites.ts b/packages/trpc/server/organisation-router/delete-organisation-member-invites.ts index 44d30e097..2e07e1c98 100644 --- a/packages/trpc/server/organisation-router/delete-organisation-member-invites.ts +++ b/packages/trpc/server/organisation-router/delete-organisation-member-invites.ts @@ -19,6 +19,13 @@ export const deleteOrganisationMemberInvitesRoute = authenticatedProcedure const { organisationId, invitationIds } = input; const userId = ctx.user.id; + ctx.logger.info({ + input: { + organisationId, + invitationIds, + }, + }); + const organisation = await prisma.organisation.findFirst({ where: buildOrganisationWhereQuery({ organisationId, diff --git a/packages/trpc/server/organisation-router/delete-organisation-member.ts b/packages/trpc/server/organisation-router/delete-organisation-member.ts index 5da41bf1e..36ff79a20 100644 --- a/packages/trpc/server/organisation-router/delete-organisation-member.ts +++ b/packages/trpc/server/organisation-router/delete-organisation-member.ts @@ -13,6 +13,13 @@ export const deleteOrganisationMemberRoute = authenticatedProcedure const { organisationId, organisationMemberId } = input; const userId = ctx.user.id; + ctx.logger.info({ + input: { + organisationId, + organisationMemberId, + }, + }); + await deleteOrganisationMembers({ userId, organisationId, diff --git a/packages/trpc/server/organisation-router/delete-organisation-members.ts b/packages/trpc/server/organisation-router/delete-organisation-members.ts index 876111913..a73991772 100644 --- a/packages/trpc/server/organisation-router/delete-organisation-members.ts +++ b/packages/trpc/server/organisation-router/delete-organisation-members.ts @@ -21,6 +21,13 @@ export const deleteOrganisationMembersRoute = authenticatedProcedure const { organisationId, organisationMemberIds } = input; const userId = ctx.user.id; + ctx.logger.info({ + input: { + organisationId, + organisationMemberIds, + }, + }); + await deleteOrganisationMembers({ userId, organisationId, diff --git a/packages/trpc/server/organisation-router/delete-organisation.ts b/packages/trpc/server/organisation-router/delete-organisation.ts index 692368cce..edf7952d4 100644 --- a/packages/trpc/server/organisation-router/delete-organisation.ts +++ b/packages/trpc/server/organisation-router/delete-organisation.ts @@ -17,6 +17,12 @@ export const deleteOrganisationRoute = authenticatedProcedure const { organisationId } = input; const { user } = ctx; + ctx.logger.info({ + input: { + organisationId, + }, + }); + const organisation = await prisma.organisation.findFirst({ where: buildOrganisationWhereQuery({ organisationId, diff --git a/packages/trpc/server/organisation-router/find-organisation-groups.ts b/packages/trpc/server/organisation-router/find-organisation-groups.ts index 1d6e39f95..8dee920e0 100644 --- a/packages/trpc/server/organisation-router/find-organisation-groups.ts +++ b/packages/trpc/server/organisation-router/find-organisation-groups.ts @@ -21,6 +21,12 @@ export const findOrganisationGroupsRoute = authenticatedProcedure input; const { user } = ctx; + ctx.logger.info({ + input: { + organisationId, + }, + }); + return await findOrganisationGroups({ userId: user.id, organisationId, diff --git a/packages/trpc/server/organisation-router/find-organisation-member-invites.ts b/packages/trpc/server/organisation-router/find-organisation-member-invites.ts index 29fe5369a..6c1b49b57 100644 --- a/packages/trpc/server/organisation-router/find-organisation-member-invites.ts +++ b/packages/trpc/server/organisation-router/find-organisation-member-invites.ts @@ -21,6 +21,12 @@ export const findOrganisationMemberInvitesRoute = authenticatedProcedure const { organisationId, query, page, perPage, status } = input; const { user } = ctx; + ctx.logger.info({ + input: { + organisationId, + }, + }); + return await findOrganisationMemberInvites({ userId: user.id, organisationId, diff --git a/packages/trpc/server/organisation-router/get-organisation.ts b/packages/trpc/server/organisation-router/get-organisation.ts index 51037550e..da8b0df6c 100644 --- a/packages/trpc/server/organisation-router/get-organisation.ts +++ b/packages/trpc/server/organisation-router/get-organisation.ts @@ -14,6 +14,12 @@ export const getOrganisationRoute = authenticatedProcedure .query(async ({ input, ctx }) => { const { organisationReference } = input; + ctx.logger.info({ + input: { + organisationReference, + }, + }); + return await getOrganisation({ userId: ctx.user.id, organisationReference, diff --git a/packages/trpc/server/organisation-router/leave-organisation.ts b/packages/trpc/server/organisation-router/leave-organisation.ts index c925adc6b..f203c61bb 100644 --- a/packages/trpc/server/organisation-router/leave-organisation.ts +++ b/packages/trpc/server/organisation-router/leave-organisation.ts @@ -17,9 +17,14 @@ export const leaveOrganisationRoute = authenticatedProcedure .output(ZLeaveOrganisationResponseSchema) .mutation(async ({ ctx, input }) => { const { organisationId } = input; - const userId = ctx.user.id; + ctx.logger.info({ + input: { + organisationId, + }, + }); + const organisation = await prisma.organisation.findFirst({ where: buildOrganisationWhereQuery({ organisationId, userId }), include: { diff --git a/packages/trpc/server/organisation-router/resend-organisation-member-invite.ts b/packages/trpc/server/organisation-router/resend-organisation-member-invite.ts index 9dbae3b2c..b3c2bdc68 100644 --- a/packages/trpc/server/organisation-router/resend-organisation-member-invite.ts +++ b/packages/trpc/server/organisation-router/resend-organisation-member-invite.ts @@ -20,6 +20,13 @@ export const resendOrganisationMemberInviteRoute = authenticatedProcedure const userId = ctx.user.id; const userName = ctx.user.name || ''; + ctx.logger.info({ + input: { + organisationId, + invitationId, + }, + }); + await resendOrganisationMemberInvitation({ userId, userName, diff --git a/packages/trpc/server/organisation-router/update-organisation-group.ts b/packages/trpc/server/organisation-router/update-organisation-group.ts index 3eef706d3..cb5c3e1c4 100644 --- a/packages/trpc/server/organisation-router/update-organisation-group.ts +++ b/packages/trpc/server/organisation-router/update-organisation-group.ts @@ -25,6 +25,12 @@ export const updateOrganisationGroupRoute = authenticatedProcedure const { id, ...data } = input; const { user } = ctx; + ctx.logger.info({ + input: { + id, + }, + }); + const organisationGroup = await prisma.organisationGroup.findFirst({ where: { id, diff --git a/packages/trpc/server/organisation-router/update-organisation-members.ts b/packages/trpc/server/organisation-router/update-organisation-members.ts index 20bd2a1b9..7309aad77 100644 --- a/packages/trpc/server/organisation-router/update-organisation-members.ts +++ b/packages/trpc/server/organisation-router/update-organisation-members.ts @@ -24,6 +24,13 @@ export const updateOrganisationMemberRoute = authenticatedProcedure const { organisationId, organisationMemberId, data } = input; const userId = ctx.user.id; + ctx.logger.info({ + input: { + organisationId, + organisationMemberId, + }, + }); + const organisation = await prisma.organisation.findFirst({ where: buildOrganisationWhereQuery({ organisationId, diff --git a/packages/trpc/server/organisation-router/update-organisation-settings.ts b/packages/trpc/server/organisation-router/update-organisation-settings.ts index 114d8d0a0..09061bf07 100644 --- a/packages/trpc/server/organisation-router/update-organisation-settings.ts +++ b/packages/trpc/server/organisation-router/update-organisation-settings.ts @@ -16,6 +16,12 @@ export const updateOrganisationSettingsRoute = authenticatedProcedure const { user } = ctx; const { organisationId, data } = input; + ctx.logger.info({ + input: { + organisationId, + }, + }); + const { // Document related settings. documentVisibility, diff --git a/packages/trpc/server/organisation-router/update-organisation.ts b/packages/trpc/server/organisation-router/update-organisation.ts index 8179315ed..bc71b9de0 100644 --- a/packages/trpc/server/organisation-router/update-organisation.ts +++ b/packages/trpc/server/organisation-router/update-organisation.ts @@ -15,9 +15,14 @@ export const updateOrganisationRoute = authenticatedProcedure .output(ZUpdateOrganisationResponseSchema) .mutation(async ({ input, ctx }) => { const { organisationId, data } = input; - const userId = ctx.user.id; + ctx.logger.info({ + input: { + organisationId, + }, + }); + // Check if organisation exists and user has access to it const existingOrganisation = await prisma.organisation.findFirst({ where: buildOrganisationWhereQuery({ diff --git a/packages/trpc/server/profile-router/router.ts b/packages/trpc/server/profile-router/router.ts index cabbbd643..927409817 100644 --- a/packages/trpc/server/profile-router/router.ts +++ b/packages/trpc/server/profile-router/router.ts @@ -23,9 +23,15 @@ export const profileRouter = router({ }); }), - getUser: adminProcedure.input(ZRetrieveUserByIdQuerySchema).query(async ({ input }) => { + getUser: adminProcedure.input(ZRetrieveUserByIdQuerySchema).query(async ({ input, ctx }) => { const { id } = input; + ctx.logger.info({ + input: { + id, + }, + }); + return await getUserById({ id }); }), @@ -53,6 +59,13 @@ export const profileRouter = router({ .mutation(async ({ input, ctx }) => { const { bytes, teamId, organisationId } = input; + ctx.logger.info({ + input: { + teamId, + organisationId, + }, + }); + let target: SetAvatarImageOptions['target'] = { type: 'user', }; diff --git a/packages/trpc/server/recipient-router/router.ts b/packages/trpc/server/recipient-router/router.ts index 03dcc0fe5..dc66306e7 100644 --- a/packages/trpc/server/recipient-router/router.ts +++ b/packages/trpc/server/recipient-router/router.ts @@ -62,6 +62,12 @@ export const recipientRouter = router({ const { teamId } = ctx; const { recipientId } = input; + ctx.logger.info({ + input: { + recipientId, + }, + }); + return await getRecipientById({ userId: ctx.user.id, teamId, @@ -88,6 +94,12 @@ export const recipientRouter = router({ const { teamId } = ctx; const { documentId, recipient } = input; + ctx.logger.info({ + input: { + documentId, + }, + }); + const createdRecipients = await createDocumentRecipients({ userId: ctx.user.id, teamId, @@ -118,6 +130,12 @@ export const recipientRouter = router({ const { teamId } = ctx; const { documentId, recipients } = input; + ctx.logger.info({ + input: { + documentId, + }, + }); + return await createDocumentRecipients({ userId: ctx.user.id, teamId, @@ -146,6 +164,12 @@ export const recipientRouter = router({ const { teamId } = ctx; const { documentId, recipient } = input; + ctx.logger.info({ + input: { + documentId, + }, + }); + const updatedRecipients = await updateDocumentRecipients({ userId: ctx.user.id, teamId, @@ -176,6 +200,12 @@ export const recipientRouter = router({ const { teamId } = ctx; const { documentId, recipients } = input; + ctx.logger.info({ + input: { + documentId, + }, + }); + return await updateDocumentRecipients({ userId: ctx.user.id, teamId, @@ -203,6 +233,12 @@ export const recipientRouter = router({ const { teamId } = ctx; const { recipientId } = input; + ctx.logger.info({ + input: { + recipientId, + }, + }); + await deleteDocumentRecipient({ userId: ctx.user.id, teamId, @@ -223,6 +259,12 @@ export const recipientRouter = router({ const { teamId } = ctx; const { documentId, recipients } = input; + ctx.logger.info({ + input: { + documentId, + }, + }); + return await setDocumentRecipients({ userId: ctx.user.id, teamId, @@ -259,6 +301,12 @@ export const recipientRouter = router({ const { teamId } = ctx; const { recipientId } = input; + ctx.logger.info({ + input: { + recipientId, + }, + }); + return await getRecipientById({ userId: ctx.user.id, teamId, @@ -285,6 +333,12 @@ export const recipientRouter = router({ const { teamId } = ctx; const { templateId, recipient } = input; + ctx.logger.info({ + input: { + templateId, + }, + }); + const createdRecipients = await createTemplateRecipients({ userId: ctx.user.id, teamId, @@ -314,6 +368,12 @@ export const recipientRouter = router({ const { teamId } = ctx; const { templateId, recipients } = input; + ctx.logger.info({ + input: { + templateId, + }, + }); + return await createTemplateRecipients({ userId: ctx.user.id, teamId, @@ -341,6 +401,12 @@ export const recipientRouter = router({ const { teamId } = ctx; const { templateId, recipient } = input; + ctx.logger.info({ + input: { + templateId, + }, + }); + const updatedRecipients = await updateTemplateRecipients({ userId: ctx.user.id, teamId, @@ -370,6 +436,12 @@ export const recipientRouter = router({ const { teamId } = ctx; const { templateId, recipients } = input; + ctx.logger.info({ + input: { + templateId, + }, + }); + return await updateTemplateRecipients({ userId: ctx.user.id, teamId, @@ -396,6 +468,12 @@ export const recipientRouter = router({ const { teamId } = ctx; const { recipientId } = input; + ctx.logger.info({ + input: { + recipientId, + }, + }); + await deleteTemplateRecipient({ recipientId, userId: ctx.user.id, @@ -415,6 +493,12 @@ export const recipientRouter = router({ const { teamId } = ctx; const { templateId, recipients } = input; + ctx.logger.info({ + input: { + templateId, + }, + }); + return await setTemplateRecipients({ userId: ctx.user.id, teamId, @@ -438,6 +522,12 @@ export const recipientRouter = router({ .mutation(async ({ input, ctx }) => { const { token, documentId, authOptions, nextSigner } = input; + ctx.logger.info({ + input: { + documentId, + }, + }); + return await completeDocumentWithToken({ token, documentId, @@ -456,6 +546,12 @@ export const recipientRouter = router({ .mutation(async ({ input, ctx }) => { const { token, documentId, reason } = input; + ctx.logger.info({ + input: { + documentId, + }, + }); + return await rejectDocumentWithToken({ token, documentId, diff --git a/packages/trpc/server/share-link-router/get-document-internal-url-for-qr-code.ts b/packages/trpc/server/share-link-router/get-document-internal-url-for-qr-code.ts index 40054877a..20764d89d 100644 --- a/packages/trpc/server/share-link-router/get-document-internal-url-for-qr-code.ts +++ b/packages/trpc/server/share-link-router/get-document-internal-url-for-qr-code.ts @@ -14,6 +14,12 @@ export const getDocumentInternalUrlForQRCodeRoute = procedure .query(async ({ input, ctx }) => { const { documentId } = input; + ctx.logger.info({ + input: { + documentId, + }, + }); + if (!ctx.user) { return null; } diff --git a/packages/trpc/server/share-link-router/router.ts b/packages/trpc/server/share-link-router/router.ts index f10424cae..3f8ac695f 100644 --- a/packages/trpc/server/share-link-router/router.ts +++ b/packages/trpc/server/share-link-router/router.ts @@ -10,6 +10,12 @@ export const shareLinkRouter = router({ .mutation(async ({ ctx, input }) => { const { documentId, token } = input; + ctx.logger.info({ + input: { + documentId, + }, + }); + if (token) { return await createOrGetShareLink({ documentId, token }); } diff --git a/packages/trpc/server/team-router/create-team-groups.ts b/packages/trpc/server/team-router/create-team-groups.ts index 415125fb6..162bd57c3 100644 --- a/packages/trpc/server/team-router/create-team-groups.ts +++ b/packages/trpc/server/team-router/create-team-groups.ts @@ -27,6 +27,13 @@ export const createTeamGroupsRoute = authenticatedProcedure const { teamId, groups } = input; const { user } = ctx; + ctx.logger.info({ + input: { + teamId, + groups, + }, + }); + const team = await prisma.team.findFirst({ where: buildTeamWhereQuery({ teamId, diff --git a/packages/trpc/server/team-router/create-team-members.ts b/packages/trpc/server/team-router/create-team-members.ts index 296a74dc9..dc7d4d756 100644 --- a/packages/trpc/server/team-router/create-team-members.ts +++ b/packages/trpc/server/team-router/create-team-members.ts @@ -21,6 +21,13 @@ export const createTeamMembersRoute = authenticatedProcedure const { teamId, organisationMembers } = input; const { user } = ctx; + ctx.logger.info({ + input: { + teamId, + organisationMembers, + }, + }); + return await createTeamMembers({ userId: user.id, teamId, diff --git a/packages/trpc/server/team-router/create-team.ts b/packages/trpc/server/team-router/create-team.ts index c9422d864..927276402 100644 --- a/packages/trpc/server/team-router/create-team.ts +++ b/packages/trpc/server/team-router/create-team.ts @@ -11,6 +11,12 @@ export const createTeamRoute = authenticatedProcedure const { teamName, teamUrl, organisationId, inheritMembers } = input; const { user } = ctx; + ctx.logger.info({ + input: { + organisationId, + }, + }); + return await createTeam({ userId: user.id, teamName, diff --git a/packages/trpc/server/team-router/delete-team-group.ts b/packages/trpc/server/team-router/delete-team-group.ts index 6cc396536..ae8005770 100644 --- a/packages/trpc/server/team-router/delete-team-group.ts +++ b/packages/trpc/server/team-router/delete-team-group.ts @@ -19,6 +19,13 @@ export const deleteTeamGroupRoute = authenticatedProcedure const { teamGroupId, teamId } = input; const { user } = ctx; + ctx.logger.info({ + input: { + teamGroupId, + teamId, + }, + }); + const team = await prisma.team.findFirst({ where: buildTeamWhereQuery({ teamId, diff --git a/packages/trpc/server/team-router/delete-team-member.ts b/packages/trpc/server/team-router/delete-team-member.ts index f5799da19..9e50217d2 100644 --- a/packages/trpc/server/team-router/delete-team-member.ts +++ b/packages/trpc/server/team-router/delete-team-member.ts @@ -20,6 +20,13 @@ export const deleteTeamMemberRoute = authenticatedProcedure const { teamId, memberId } = input; const { user } = ctx; + ctx.logger.info({ + input: { + teamId, + memberId, + }, + }); + const team = await prisma.team.findFirst({ where: buildTeamWhereQuery({ teamId, diff --git a/packages/trpc/server/team-router/delete-team.ts b/packages/trpc/server/team-router/delete-team.ts index 46bec4b8a..2624a902b 100644 --- a/packages/trpc/server/team-router/delete-team.ts +++ b/packages/trpc/server/team-router/delete-team.ts @@ -11,6 +11,12 @@ export const deleteTeamRoute = authenticatedProcedure const { teamId } = input; const { user } = ctx; + ctx.logger.info({ + input: { + teamId, + }, + }); + await deleteTeam({ userId: user.id, teamId, diff --git a/packages/trpc/server/team-router/find-team-groups.ts b/packages/trpc/server/team-router/find-team-groups.ts index 5ac66a859..3ef9e5a6c 100644 --- a/packages/trpc/server/team-router/find-team-groups.ts +++ b/packages/trpc/server/team-router/find-team-groups.ts @@ -20,6 +20,13 @@ export const findTeamGroupsRoute = authenticatedProcedure const { teamId, types, query, page, perPage, teamGroupId, organisationRoles } = input; const { user } = ctx; + ctx.logger.info({ + input: { + teamId, + teamGroupId, + }, + }); + return await findTeamGroups({ userId: user.id, teamId, diff --git a/packages/trpc/server/team-router/find-team-members.ts b/packages/trpc/server/team-router/find-team-members.ts index c9550b0e4..91d046382 100644 --- a/packages/trpc/server/team-router/find-team-members.ts +++ b/packages/trpc/server/team-router/find-team-members.ts @@ -13,6 +13,12 @@ export const findTeamMembersRoute = authenticatedProcedure const { teamId, query, page, perPage } = input; const { user } = ctx; + ctx.logger.info({ + input: { + teamId, + }, + }); + return await findTeamMembers({ userId: user.id, teamId, diff --git a/packages/trpc/server/team-router/find-teams.ts b/packages/trpc/server/team-router/find-teams.ts index f6d9cc4e7..e4466d96c 100644 --- a/packages/trpc/server/team-router/find-teams.ts +++ b/packages/trpc/server/team-router/find-teams.ts @@ -11,5 +11,11 @@ export const findTeamsRoute = authenticatedProcedure const { organisationId } = input; const { user } = ctx; + ctx.logger.info({ + input: { + organisationId, + }, + }); + return findTeams({ userId: user.id, organisationId }); }); diff --git a/packages/trpc/server/team-router/get-team-members.ts b/packages/trpc/server/team-router/get-team-members.ts index a98c71dca..f941e497b 100644 --- a/packages/trpc/server/team-router/get-team-members.ts +++ b/packages/trpc/server/team-router/get-team-members.ts @@ -14,6 +14,12 @@ export const getTeamMembersRoute = authenticatedProcedure const { teamId } = input; const { user } = ctx; + ctx.logger.info({ + input: { + teamId, + }, + }); + return await getTeamMembers({ userId: user.id, teamId, diff --git a/packages/trpc/server/team-router/get-team.ts b/packages/trpc/server/team-router/get-team.ts index f36abb293..3b7db0871 100644 --- a/packages/trpc/server/team-router/get-team.ts +++ b/packages/trpc/server/team-router/get-team.ts @@ -8,8 +8,16 @@ export const getTeamRoute = authenticatedProcedure .input(ZGetTeamRequestSchema) .output(ZGetTeamResponseSchema) .query(async ({ input, ctx }) => { + const { teamReference } = input; + + ctx.logger.info({ + input: { + teamReference, + }, + }); + return await getTeam({ - teamReference: input.teamReference, + teamReference, userId: ctx.user.id, }); }); diff --git a/packages/trpc/server/team-router/router.ts b/packages/trpc/server/team-router/router.ts index aeb1fa8af..f982a4e1e 100644 --- a/packages/trpc/server/team-router/router.ts +++ b/packages/trpc/server/team-router/router.ts @@ -61,6 +61,12 @@ export const teamRouter = router({ update: authenticatedProcedure .input(ZUpdateTeamEmailMutationSchema) .mutation(async ({ input, ctx }) => { + ctx.logger.info({ + input: { + teamId: input.teamId, + }, + }); + return await updateTeamEmail({ userId: ctx.user.id, ...input, @@ -69,39 +75,71 @@ export const teamRouter = router({ delete: authenticatedProcedure .input(ZDeleteTeamEmailMutationSchema) .mutation(async ({ input, ctx }) => { + const { teamId } = input; + + ctx.logger.info({ + input: { + teamId, + }, + }); + return await deleteTeamEmail({ userId: ctx.user.id, userEmail: ctx.user.email, - ...input, + teamId, }); }), verification: { send: authenticatedProcedure .input(ZCreateTeamEmailVerificationMutationSchema) .mutation(async ({ input, ctx }) => { + const { teamId, email, name } = input; + + ctx.logger.info({ + input: { + teamId, + }, + }); + return await createTeamEmailVerification({ - teamId: input.teamId, + teamId, userId: ctx.user.id, data: { - email: input.email, - name: input.name, + email, + name, }, }); }), resend: authenticatedProcedure .input(ZResendTeamEmailVerificationMutationSchema) .mutation(async ({ input, ctx }) => { + const { teamId } = input; + + ctx.logger.info({ + input: { + teamId, + }, + }); + await resendTeamEmailVerification({ userId: ctx.user.id, - ...input, + teamId, }); }), delete: authenticatedProcedure .input(ZDeleteTeamEmailVerificationMutationSchema) .mutation(async ({ input, ctx }) => { + const { teamId } = input; + + ctx.logger.info({ + input: { + teamId, + }, + }); + return await deleteTeamEmailVerification({ userId: ctx.user.id, - ...input, + teamId, }); }), }, diff --git a/packages/trpc/server/team-router/update-team-group.ts b/packages/trpc/server/team-router/update-team-group.ts index 4c10ff18b..5090ef9f0 100644 --- a/packages/trpc/server/team-router/update-team-group.ts +++ b/packages/trpc/server/team-router/update-team-group.ts @@ -19,6 +19,15 @@ export const updateTeamGroupRoute = authenticatedProcedure const { id, data } = input; const { user } = ctx; + ctx.logger.info({ + input: { + id, + data: { + teamRole: data.teamRole, + }, + }, + }); + const teamGroup = await prisma.teamGroup.findFirst({ where: { id, diff --git a/packages/trpc/server/team-router/update-team-member.ts b/packages/trpc/server/team-router/update-team-member.ts index e418fb5be..c21e2e371 100644 --- a/packages/trpc/server/team-router/update-team-member.ts +++ b/packages/trpc/server/team-router/update-team-member.ts @@ -22,6 +22,13 @@ export const updateTeamMemberRoute = authenticatedProcedure const { teamId, memberId, data } = input; const userId = ctx.user.id; + ctx.logger.info({ + input: { + teamId, + memberId, + }, + }); + const team = await prisma.team.findFirst({ where: { AND: [ diff --git a/packages/trpc/server/team-router/update-team-settings.ts b/packages/trpc/server/team-router/update-team-settings.ts index 7c6083645..72082a9a1 100644 --- a/packages/trpc/server/team-router/update-team-settings.ts +++ b/packages/trpc/server/team-router/update-team-settings.ts @@ -16,6 +16,12 @@ export const updateTeamSettingsRoute = authenticatedProcedure const { user } = ctx; const { teamId, data } = input; + ctx.logger.info({ + input: { + teamId, + }, + }); + const { // Document related settings. documentVisibility, diff --git a/packages/trpc/server/team-router/update-team.ts b/packages/trpc/server/team-router/update-team.ts index 8aa9ab197..6dae1f829 100644 --- a/packages/trpc/server/team-router/update-team.ts +++ b/packages/trpc/server/team-router/update-team.ts @@ -13,6 +13,12 @@ export const updateTeamRoute = authenticatedProcedure const { name, url, profileBio, profileEnabled } = data; + ctx.logger.info({ + input: { + teamId, + }, + }); + if (name || url) { await updateTeam({ userId: ctx.user.id, diff --git a/packages/trpc/server/template-router/router.ts b/packages/trpc/server/template-router/router.ts index b10412c58..2fc8f6f86 100644 --- a/packages/trpc/server/template-router/router.ts +++ b/packages/trpc/server/template-router/router.ts @@ -67,6 +67,12 @@ export const templateRouter = router({ .query(async ({ input, ctx }) => { const { teamId } = ctx; + ctx.logger.info({ + input: { + folderId: input.folderId, + }, + }); + return await findTemplates({ userId: ctx.user.id, teamId, @@ -92,6 +98,12 @@ export const templateRouter = router({ const { teamId } = ctx; const { templateId } = input; + ctx.logger.info({ + input: { + templateId, + }, + }); + return await getTemplateById({ id: templateId, userId: ctx.user.id, @@ -120,6 +132,12 @@ export const templateRouter = router({ const { teamId } = ctx; const { title, templateDocumentDataId, folderId } = input; + ctx.logger.info({ + input: { + folderId, + }, + }); + return await createTemplate({ userId: ctx.user.id, teamId, @@ -146,9 +164,14 @@ export const templateRouter = router({ .mutation(async ({ input, ctx }) => { const { teamId } = ctx; const { templateId, data, meta } = input; - const userId = ctx.user.id; + ctx.logger.info({ + input: { + templateId, + }, + }); + return await updateTemplate({ userId, teamId, @@ -176,6 +199,12 @@ export const templateRouter = router({ const { teamId } = ctx; const { templateId } = input; + ctx.logger.info({ + input: { + templateId, + }, + }); + return await duplicateTemplate({ userId: ctx.user.id, teamId, @@ -200,9 +229,14 @@ export const templateRouter = router({ .mutation(async ({ input, ctx }) => { const { teamId } = ctx; const { templateId } = input; - const userId = ctx.user.id; + ctx.logger.info({ + input: { + templateId, + }, + }); + await deleteTemplate({ userId, id: templateId, teamId }); return ZGenericSuccessResponse; @@ -228,6 +262,12 @@ export const templateRouter = router({ const { templateId, recipients, distributeDocument, customDocumentDataId, prefillFields } = input; + ctx.logger.info({ + input: { + templateId, + }, + }); + const limits = await getServerLimits({ userId: ctx.user.id, teamId }); if (limits.remaining.documents === 0) { @@ -291,6 +331,12 @@ export const templateRouter = router({ templateUpdatedAt, } = input; + ctx.logger.info({ + input: { + directTemplateToken, + }, + }); + return await createDocumentFromDirectTemplate({ directRecipientName, directRecipientEmail, @@ -330,6 +376,13 @@ export const templateRouter = router({ const userId = ctx.user.id; + ctx.logger.info({ + input: { + templateId, + directRecipientId, + }, + }); + const template = await getTemplateById({ id: templateId, teamId, userId: ctx.user.id }); const limits = await getServerLimits({ userId: ctx.user.id, teamId: template.teamId }); @@ -364,6 +417,12 @@ export const templateRouter = router({ const userId = ctx.user.id; + ctx.logger.info({ + input: { + templateId, + }, + }); + await deleteTemplateDirectLink({ userId, teamId, templateId }); return ZGenericSuccessResponse; @@ -390,6 +449,12 @@ export const templateRouter = router({ const userId = ctx.user.id; + ctx.logger.info({ + input: { + templateId, + }, + }); + return await toggleTemplateDirectLink({ userId, teamId, templateId, enabled }); }), @@ -402,6 +467,13 @@ export const templateRouter = router({ const { templateId, teamId, csv, sendImmediately } = input; const { user } = ctx; + ctx.logger.info({ + input: { + templateId, + teamId, + }, + }); + if (csv.length > 4 * 1024 * 1024) { throw new TRPCError({ code: 'BAD_REQUEST', diff --git a/packages/trpc/server/trpc.ts b/packages/trpc/server/trpc.ts index 737df2a36..d7ff12eee 100644 --- a/packages/trpc/server/trpc.ts +++ b/packages/trpc/server/trpc.ts @@ -4,7 +4,9 @@ import type { AnyZodObject } from 'zod'; import { AppError, genericErrorCodeToTrpcErrorCodeMap } from '@documenso/lib/errors/app-error'; import { getApiTokenByToken } from '@documenso/lib/server-only/public-api/get-api-token-by-token'; +import type { TrpcApiLog } from '@documenso/lib/types/api-logs'; import type { ApiRequestMetadata } from '@documenso/lib/universal/extract-request-metadata'; +import { alphaid } from '@documenso/lib/universal/id'; import { isAdmin } from '@documenso/lib/utils/is-admin'; import type { TrpcContext } from './context'; @@ -66,11 +68,13 @@ const t = initTRPC * Middlewares */ export const authenticatedMiddleware = t.middleware(async ({ ctx, next, path }) => { - const logger = ctx.logger.child({ + const infoToLog: TrpcApiLog = { path, auth: ctx.metadata.auth, source: ctx.metadata.source, - }); + trpcMiddleware: 'authenticated', + unverifiedTeamId: ctx.teamId, + }; const authorizationHeader = ctx.req.headers.get('authorization'); @@ -85,10 +89,11 @@ export const authenticatedMiddleware = t.middleware(async ({ ctx, next, path }) const apiToken = await getApiTokenByToken({ token }); - logger.info({ + ctx.logger.info({ + ...infoToLog, userId: apiToken.user.id, apiTokenId: apiToken.id, - }); + } satisfies TrpcApiLog); return await next({ ctx: { @@ -122,14 +127,21 @@ export const authenticatedMiddleware = t.middleware(async ({ ctx, next, path }) }); } - logger.info({ + // Recreate the logger with a sub request ID to differentiate between batched requests. + const trpcSessionLogger = ctx.logger.child({ + nonBatchedRequestId: alphaid(), + }); + + trpcSessionLogger.info({ + ...infoToLog, userId: ctx.user.id, apiTokenId: null, - }); + } satisfies TrpcApiLog); return await next({ ctx: { ...ctx, + logger: trpcSessionLogger, user: ctx.user, session: ctx.session, metadata: { @@ -145,10 +157,26 @@ export const authenticatedMiddleware = t.middleware(async ({ ctx, next, path }) }); }); -export const maybeAuthenticatedMiddleware = t.middleware(async ({ ctx, next }) => { +export const maybeAuthenticatedMiddleware = t.middleware(async ({ ctx, next, path }) => { + // Recreate the logger with a sub request ID to differentiate between batched requests. + const trpcSessionLogger = ctx.logger.child({ + nonBatchedRequestId: alphaid(), + }); + + ctx.logger.info({ + path, + auth: ctx.metadata.auth, + source: ctx.metadata.source, + userId: ctx.user?.id, + apiTokenId: null, + trpcMiddleware: 'maybeAuthenticated', + unverifiedTeamId: ctx.teamId, + } satisfies TrpcApiLog); + return await next({ ctx: { ...ctx, + logger: trpcSessionLogger, user: ctx.user, session: ctx.session, metadata: { @@ -166,7 +194,7 @@ export const maybeAuthenticatedMiddleware = t.middleware(async ({ ctx, next }) = }); }); -export const adminMiddleware = t.middleware(async ({ ctx, next }) => { +export const adminMiddleware = t.middleware(async ({ ctx, next, path }) => { if (!ctx.session || !ctx.user) { throw new TRPCError({ code: 'UNAUTHORIZED', @@ -183,9 +211,24 @@ export const adminMiddleware = t.middleware(async ({ ctx, next }) => { }); } + // Recreate the logger with a sub request ID to differentiate between batched requests. + const trpcSessionLogger = ctx.logger.child({ + nonBatchedRequestId: alphaid(), + }); + + trpcSessionLogger.info({ + path, + auth: ctx.metadata.auth, + source: ctx.metadata.source, + userId: ctx.user.id, + apiTokenId: null, + trpcMiddleware: 'admin', + } satisfies TrpcApiLog); + return await next({ ctx: { ...ctx, + logger: trpcSessionLogger, user: ctx.user, session: ctx.session, metadata: { @@ -201,11 +244,34 @@ export const adminMiddleware = t.middleware(async ({ ctx, next }) => { }); }); +export const procedureMiddleware = t.middleware(async ({ ctx, next, path }) => { + // Recreate the logger with a sub request ID to differentiate between batched requests. + const trpcSessionLogger = ctx.logger.child({ + nonBatchedRequestId: alphaid(), + }); + + trpcSessionLogger.info({ + path, + auth: ctx.metadata.auth, + source: ctx.metadata.source, + userId: ctx.user?.id, + apiTokenId: null, + trpcMiddleware: 'procedure', + } satisfies TrpcApiLog); + + return await next({ + ctx: { + ...ctx, + logger: trpcSessionLogger, + }, + }); +}); + /** * Routers and Procedures */ export const router = t.router; -export const procedure = t.procedure; +export const procedure = t.procedure.use(procedureMiddleware); export const authenticatedProcedure = t.procedure.use(authenticatedMiddleware); // While this is functionally the same as `procedure`, it's useful for indicating purpose export const maybeAuthenticatedProcedure = t.procedure.use(maybeAuthenticatedMiddleware); diff --git a/packages/trpc/server/webhook-router/router.ts b/packages/trpc/server/webhook-router/router.ts index 9323963ed..ce06e4e43 100644 --- a/packages/trpc/server/webhook-router/router.ts +++ b/packages/trpc/server/webhook-router/router.ts @@ -19,6 +19,12 @@ export const webhookRouter = router({ .query(async ({ ctx, input }) => { const { teamId } = input; + ctx.logger.info({ + input: { + teamId, + }, + }); + return await getWebhooksByTeamId(teamId, ctx.user.id); }), @@ -27,6 +33,13 @@ export const webhookRouter = router({ .query(async ({ input, ctx }) => { const { id, teamId } = input; + ctx.logger.info({ + input: { + id, + teamId, + }, + }); + return await getWebhookById({ id, userId: ctx.user.id, @@ -39,6 +52,12 @@ export const webhookRouter = router({ .mutation(async ({ input, ctx }) => { const { enabled, eventTriggers, secret, webhookUrl, teamId } = input; + ctx.logger.info({ + input: { + teamId, + }, + }); + return await createWebhook({ enabled, secret, @@ -54,6 +73,13 @@ export const webhookRouter = router({ .mutation(async ({ input, ctx }) => { const { id, teamId } = input; + ctx.logger.info({ + input: { + id, + teamId, + }, + }); + return await deleteWebhookById({ id, teamId, @@ -66,6 +92,13 @@ export const webhookRouter = router({ .mutation(async ({ input, ctx }) => { const { id, teamId, ...data } = input; + ctx.logger.info({ + input: { + id, + teamId, + }, + }); + return await editWebhook({ id, data, diff --git a/packages/trpc/utils/trpc-error-handler.ts b/packages/trpc/utils/trpc-error-handler.ts index aa488f612..1502d4d6e 100644 --- a/packages/trpc/utils/trpc-error-handler.ts +++ b/packages/trpc/utils/trpc-error-handler.ts @@ -1,14 +1,14 @@ import type { ErrorHandlerOptions } from '@trpc/server/unstable-core-do-not-import'; import { AppError, AppErrorCode } from '@documenso/lib/errors/app-error'; -import { buildLogger } from '@documenso/lib/utils/logger-legacy'; +import { logger } from '@documenso/lib/utils/logger'; -const logger = buildLogger(); +import type { TrpcContext } from '../server/context'; // Parameters[0]['onError']>>[0], // :-) export const handleTrpcRouterError = ( - { error, path }: Pick, 'error' | 'path'>, - source: 'trpc' | 'apiV1' | 'apiV2', + { error, ctx }: Pick, 'error' | 'path' | 'ctx'>, + _source: 'trpc' | 'apiV1' | 'apiV2', ) => { const appError = AppError.parseError(error.cause || error); @@ -23,16 +23,16 @@ export const handleTrpcRouterError = ( // not an AppError. const isLoggableTrpcError = !isAppError && errorCodesToAlertOn.includes(error.code); - if (isLoggableAppError || isLoggableTrpcError) { - console.error(error); + const errorLogger = (ctx?.logger || logger).child({ + status: 'error', + appError: AppError.toJSON(appError), + }); - logger.error(error, { - method: path, - context: { - source, - appError: AppError.toJSON(appError), - }, - }); + // Only fully log the error on certain conditions since some errors are expected. + if (isLoggableAppError || isLoggableTrpcError) { + errorLogger.error(error); + } else { + errorLogger.info('TRPC_ERROR_HANDLER'); } }; diff --git a/packages/tsconfig/process-env.d.ts b/packages/tsconfig/process-env.d.ts index 143dbe30d..7962031f5 100644 --- a/packages/tsconfig/process-env.d.ts +++ b/packages/tsconfig/process-env.d.ts @@ -15,6 +15,8 @@ declare namespace NodeJS { NEXT_PRIVATE_ENCRYPTION_KEY: string; NEXT_PRIVATE_ENCRYPTION_SECONDARY_KEY: string; + NEXT_PRIVATE_LOGGER_FILE_PATH?: string; + NEXT_PRIVATE_STRIPE_API_KEY: string; NEXT_PRIVATE_STRIPE_WEBHOOK_SECRET: string; @@ -77,8 +79,6 @@ declare namespace NodeJS { NEXT_PRIVATE_INNGEST_APP_ID?: string; NEXT_PRIVATE_INNGEST_EVENT_KEY?: string; - NEXT_PRIVATE_LOGGER_HONEY_BADGER_API_KEY?: string; - POSTGRES_URL?: string; DATABASE_URL?: string; POSTGRES_PRISMA_URL?: string; diff --git a/turbo.json b/turbo.json index db492e33a..77c668b35 100644 --- a/turbo.json +++ b/turbo.json @@ -50,6 +50,7 @@ "NEXT_PUBLIC_DOCUMENT_SIZE_UPLOAD_LIMIT", "NEXT_PRIVATE_DATABASE_URL", "NEXT_PRIVATE_DIRECT_DATABASE_URL", + "NEXT_PRIVATE_LOGGER_FILE_PATH", "NEXT_PRIVATE_SIGNING_TRANSPORT", "NEXT_PRIVATE_SIGNING_PASSPHRASE", "NEXT_PRIVATE_SIGNING_LOCAL_FILE_PATH", @@ -104,7 +105,6 @@ "NEXT_PRIVATE_BROWSERLESS_URL", "NEXT_PRIVATE_JOBS_PROVIDER", "NEXT_PRIVATE_INNGEST_APP_ID", - "NEXT_PRIVATE_LOGGER_HONEY_BADGER_API_KEY", "INNGEST_EVENT_KEY", "NEXT_PRIVATE_INNGEST_EVENT_KEY", "CI",