feat: add more api logs (#1870)

Adds more detailed API logging using Pino
This commit is contained in:
David Nguyen
2025-06-30 19:46:32 +10:00
committed by GitHub
parent 0cc729e9bd
commit 7487399123
74 changed files with 1395 additions and 544 deletions

View File

@ -127,4 +127,6 @@ E2E_TEST_AUTHENTICATE_USER_EMAIL="testuser@mail.com"
E2E_TEST_AUTHENTICATE_USER_PASSWORD="test_Password123" E2E_TEST_AUTHENTICATE_USER_PASSWORD="test_Password123"
# [[LOGGER]] # [[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=

3
.gitignore vendored
View File

@ -50,3 +50,6 @@ yarn-error.log*
!.vscode/tasks.json !.vscode/tasks.json
!.vscode/launch.json !.vscode/launch.json
!.vscode/extensions.json !.vscode/extensions.json
# logs
logs.json

137
package-lock.json generated
View File

@ -3073,36 +3073,6 @@
"integrity": "sha512-lhqDEAvWixy3bZ+UOYbPwUbBkwBq5C1LAJ/xPC8Oi+lL54oyakv/npbA0aU2hgCsx/1NUd4IBvV03+aUBWxerw==", "integrity": "sha512-lhqDEAvWixy3bZ+UOYbPwUbBkwBq5C1LAJ/xPC8Oi+lL54oyakv/npbA0aU2hgCsx/1NUd4IBvV03+aUBWxerw==",
"license": "MIT" "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": { "node_modules/@hono/node-server": {
"version": "1.14.2", "version": "1.14.2",
"resolved": "https://registry.npmjs.org/@hono/node-server/-/node-server-1.14.2.tgz", "resolved": "https://registry.npmjs.org/@hono/node-server/-/node-server-1.14.2.tgz",
@ -11715,16 +11685,6 @@
"@babel/types": "^7.20.7" "@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": { "node_modules/@types/bunyan": {
"version": "1.8.11", "version": "1.8.11",
"resolved": "https://registry.npmjs.org/@types/bunyan/-/bunyan-1.8.11.tgz", "resolved": "https://registry.npmjs.org/@types/bunyan/-/bunyan-1.8.11.tgz",
@ -11845,30 +11805,6 @@
"@types/estree": "*" "@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": { "node_modules/@types/formidable": {
"version": "2.0.6", "version": "2.0.6",
"resolved": "https://registry.npmjs.org/@types/formidable/-/formidable-2.0.6.tgz", "resolved": "https://registry.npmjs.org/@types/formidable/-/formidable-2.0.6.tgz",
@ -11898,12 +11834,6 @@
"hoist-non-react-statics": "^3.3.0" "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": { "node_modules/@types/istanbul-lib-coverage": {
"version": "2.0.6", "version": "2.0.6",
"resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.6.tgz", "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.6.tgz",
@ -11977,12 +11907,6 @@
"@types/node": "*" "@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": { "node_modules/@types/minimist": {
"version": "1.2.5", "version": "1.2.5",
"resolved": "https://registry.npmjs.org/@types/minimist/-/minimist-1.2.5.tgz", "resolved": "https://registry.npmjs.org/@types/minimist/-/minimist-1.2.5.tgz",
@ -12070,12 +11994,6 @@
"integrity": "sha512-gNMvNH49DJ7OJYv+KAKn0Xp45p8PLl6zo2YnvDIbTd4J6MER2BmWN49TG7n9LvkyihINxeKW8+3bfS2yDC9dzQ==", "integrity": "sha512-gNMvNH49DJ7OJYv+KAKn0Xp45p8PLl6zo2YnvDIbTd4J6MER2BmWN49TG7n9LvkyihINxeKW8+3bfS2yDC9dzQ==",
"license": "MIT" "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": { "node_modules/@types/ramda": {
"version": "0.30.2", "version": "0.30.2",
"resolved": "https://registry.npmjs.org/@types/ramda/-/ramda-0.30.2.tgz", "resolved": "https://registry.npmjs.org/@types/ramda/-/ramda-0.30.2.tgz",
@ -12085,12 +12003,6 @@
"types-ramda": "^0.30.1" "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": { "node_modules/@types/react": {
"version": "18.3.5", "version": "18.3.5",
"resolved": "https://registry.npmjs.org/@types/react/-/react-18.3.5.tgz", "resolved": "https://registry.npmjs.org/@types/react/-/react-18.3.5.tgz",
@ -12124,27 +12036,6 @@
"integrity": "sha512-wWKOClTTiizcZhXnPY4wikVAwmdYHp8q6DmC+EJUzAMsycb7HB32Kh9RN4+0gExjmPmZSAQjgURXIGATPegAvA==", "integrity": "sha512-wWKOClTTiizcZhXnPY4wikVAwmdYHp8q6DmC+EJUzAMsycb7HB32Kh9RN4+0gExjmPmZSAQjgURXIGATPegAvA==",
"license": "MIT" "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": { "node_modules/@types/shimmer": {
"version": "1.2.0", "version": "1.2.0",
"resolved": "https://registry.npmjs.org/@types/shimmer/-/shimmer-1.2.0.tgz", "resolved": "https://registry.npmjs.org/@types/shimmer/-/shimmer-1.2.0.tgz",
@ -21899,12 +21790,6 @@
"integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==",
"license": "MIT" "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": { "node_modules/json-parse-even-better-errors": {
"version": "2.3.1", "version": "2.3.1",
"resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", "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==", "integrity": "sha512-+L3ccpzibovGXFK+Ap/f8LOS0ahMrHTf3xu7mMLSpEGU0EO9ucaysSylKo9eRDFNhWve/y275iPmIZ4z39a9iA==",
"license": "MIT" "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": { "node_modules/statuses": {
"version": "2.0.1", "version": "2.0.1",
"resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz",
@ -36180,7 +36044,6 @@
"@documenso/email": "*", "@documenso/email": "*",
"@documenso/prisma": "*", "@documenso/prisma": "*",
"@documenso/signing": "*", "@documenso/signing": "*",
"@honeybadger-io/js": "^6.10.1",
"@lingui/core": "^5.2.0", "@lingui/core": "^5.2.0",
"@lingui/macro": "^5.2.0", "@lingui/macro": "^5.2.0",
"@lingui/react": "^5.2.0", "@lingui/react": "^5.2.0",

View File

@ -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; const { id: documentId } = args.params;
logger.info({
input: {
id: documentId,
},
});
try { try {
const document = await getDocumentById({ const document = await getDocumentById({
documentId: Number(documentId), 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 { id: documentId } = args.params;
const { downloadOriginalDocument } = args.query; const { downloadOriginalDocument } = args.query;
logger.info({
input: {
id: documentId,
},
});
try { try {
if (process.env.NEXT_PUBLIC_UPLOAD_TRANSPORT !== 's3') { if (process.env.NEXT_PUBLIC_UPLOAD_TRANSPORT !== 's3') {
return { 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; const { id: documentId } = args.params;
logger.info({
input: {
id: documentId,
},
});
try { try {
const document = await getDocumentById({ const document = await getDocumentById({
documentId: Number(documentId), 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; const { id: templateId } = args.params;
logger.info({
input: {
id: templateId,
},
});
try { try {
const deletedTemplate = await deleteTemplate({ const deletedTemplate = await deleteTemplate({
id: Number(templateId), 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; const { id: templateId } = args.params;
logger.info({
input: {
id: templateId,
},
});
try { try {
const template = await getTemplateById({ const template = await getTemplateById({
id: Number(templateId), id: Number(templateId),
@ -463,202 +493,224 @@ export const ApiContractV1Implementation = tsr.router(ApiContractV1, {
} }
}), }),
createDocumentFromTemplate: authenticatedMiddleware(async (args, user, team, { metadata }) => { createDocumentFromTemplate: authenticatedMiddleware(
const { body, params } = args; async (args, user, team, { logger, metadata }) => {
const { body, params } = args;
const { remaining } = await getServerLimits({ userId: user.id, teamId: team?.id }); logger.info({
input: {
if (remaining.documents <= 0) { templateId: params.templateId,
return {
status: 400,
body: {
message: 'You have reached the maximum number of documents allowed for this month',
}, },
};
}
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({ const { remaining } = await getServerLimits({ userId: user.id, teamId: team?.id });
name: fileName,
type: 'application/pdf',
arrayBuffer: async () => Promise.resolve(prefilled),
});
documentDataId = newDocumentData.id; if (remaining.documents <= 0) {
} return {
status: 400,
await updateDocument({ body: {
documentId: document.id, message: 'You have reached the maximum number of documents allowed for this month',
userId: user.id,
teamId: team?.id,
data: {
title: fileName,
externalId: body.externalId || null,
formValues: body.formValues,
documentData: {
connect: {
id: documentDataId,
}, },
}, };
}, }
});
if (body.meta) { const templateId = Number(params.templateId);
await upsertDocumentMeta({
documentId: document.id,
userId: user.id,
teamId: team?.id,
...body.meta,
requestMetadata: metadata,
});
}
if (body.authOptions) { const fileName = body.title.endsWith('.pdf') ? body.title : `${body.title}.pdf`;
await updateDocumentSettings({
documentId: document.id,
userId: user.id,
teamId: team?.id,
data: body.authOptions,
requestMetadata: metadata,
});
}
return { const document = await createDocumentFromTemplateLegacy({
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<ReturnType<typeof createDocumentFromTemplate>> | null = null;
try {
document = await createDocumentFromTemplate({
templateId, templateId,
externalId: body.externalId || null,
userId: user.id, userId: user.id,
teamId: team?.id, teamId: team?.id,
recipients: body.recipients, 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({ let documentDataId = document.documentDataId;
name: fileName,
type: 'application/pdf', if (body.formValues) {
arrayBuffer: async () => Promise.resolve(prefilled), 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({ await updateDocument({
documentId: document.id, documentId: document.id,
userId: user.id, userId: user.id,
teamId: team?.id, teamId: team?.id,
data: { data: {
title: fileName,
externalId: body.externalId || null,
formValues: body.formValues, formValues: body.formValues,
documentData: { documentData: {
connect: { connect: {
id: newDocumentData.id, id: documentDataId,
}, },
}, },
}, },
}); });
}
if (body.authOptions) { if (body.meta) {
await updateDocumentSettings({ await upsertDocumentMeta({
documentId: document.id, documentId: document.id,
userId: user.id, userId: user.id,
teamId: team?.id, teamId: team?.id,
data: body.authOptions, ...body.meta,
requestMetadata: metadata, 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 { const { remaining } = await getServerLimits({ userId: user.id, teamId: team?.id });
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, { 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<ReturnType<typeof createDocumentFromTemplate>> | 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 { id: documentId } = args.params;
const { sendEmail, sendCompletionEmails } = args.body; const { sendEmail, sendCompletionEmails } = args.body;
logger.info({
input: {
id: documentId,
},
});
try { try {
const document = await getDocumentById({ const document = await getDocumentById({
documentId: Number(documentId), 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 { id: documentId } = args.params;
const { recipients } = args.body; const { recipients } = args.body;
logger.info({
input: {
id: documentId,
},
});
try { try {
await resendDocument({ await resendDocument({
userId: user.id, 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 { id: documentId } = args.params;
const { name, email, role, authOptions, signingOrder } = args.body; const { name, email, role, authOptions, signingOrder } = args.body;
logger.info({
input: {
id: documentId,
},
});
const document = await getDocumentById({ const document = await getDocumentById({
documentId: Number(documentId), documentId: Number(documentId),
userId: user.id, 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 { id: documentId, recipientId } = args.params;
const { name, email, role, authOptions, signingOrder } = args.body; const { name, email, role, authOptions, signingOrder } = args.body;
logger.info({
input: {
id: documentId,
recipientId,
},
});
const document = await getDocumentById({ const document = await getDocumentById({
documentId: Number(documentId), documentId: Number(documentId),
userId: user.id, 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; const { id: documentId, recipientId } = args.params;
logger.info({
input: {
id: documentId,
recipientId,
},
});
const document = await getDocumentById({ const document = await getDocumentById({
documentId: Number(documentId), documentId: Number(documentId),
userId: user.id, 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; const { id: documentId } = args.params;
logger.info({
input: {
id: documentId,
},
});
const fields = Array.isArray(args.body) ? args.body : [args.body]; const fields = Array.isArray(args.body) ? args.body : [args.body];
const document = await prisma.document.findFirst({ 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 { id: documentId, fieldId } = args.params;
const { recipientId, type, pageNumber, pageWidth, pageHeight, pageX, pageY, fieldMeta } = const { recipientId, type, pageNumber, pageWidth, pageHeight, pageX, pageY, fieldMeta } =
args.body; args.body;
logger.info({
input: {
id: documentId,
fieldId,
},
});
const document = await getDocumentById({ const document = await getDocumentById({
documentId: Number(documentId), documentId: Number(documentId),
userId: user.id, 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; const { id: documentId, fieldId } = args.params;
logger.info({
input: {
id: documentId,
fieldId,
},
});
const document = await getDocumentById({ const document = await getDocumentById({
documentId: Number(documentId), documentId: Number(documentId),
userId: user.id, userId: user.id,

View File

@ -1,10 +1,13 @@
import type { Team, User } from '@prisma/client'; import type { Team, User } from '@prisma/client';
import type { TsRestRequest } from '@ts-rest/serverless'; import type { TsRestRequest } from '@ts-rest/serverless';
import type { Logger } from 'pino';
import { AppError, AppErrorCode } from '@documenso/lib/errors/app-error'; import { AppError, AppErrorCode } from '@documenso/lib/errors/app-error';
import { getApiTokenByToken } from '@documenso/lib/server-only/public-api/get-api-token-by-token'; 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 type { ApiRequestMetadata } from '@documenso/lib/universal/extract-request-metadata';
import { extractRequestMetadata } 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'; import { logger } from '@documenso/lib/utils/logger';
type B = { type B = {
@ -28,10 +31,24 @@ export const authenticatedMiddleware = <
args: T & { req: TsRestRequest }, args: T & { req: TsRestRequest },
user: Pick<User, 'id' | 'email' | 'name' | 'disabled'>, user: Pick<User, 'id' | 'email' | 'name' | 'disabled'>,
team: Team, team: Team,
options: { metadata: ApiRequestMetadata }, options: { metadata: ApiRequestMetadata; logger: Logger },
) => Promise<R>, ) => Promise<R>,
) => { ) => {
return async (args: T, { request }: B) => { 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 { try {
const { authorization } = args.headers; 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 = { const metadata: ApiRequestMetadata = {
requestMetadata: extractRequestMetadata(request), requestMetadata,
source: 'apiV1', source: 'apiV1',
auth: 'api', auth: 'api',
auditUser: { 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( return await handler(
{ {
...args, ...args,
@ -81,10 +93,12 @@ export const authenticatedMiddleware = <
}, },
apiToken.user, apiToken.user,
apiToken.team, apiToken.team,
{ metadata }, { metadata, logger: apiLogger },
); );
} catch (err) { } catch (err) {
console.log({ err: err }); console.log({ err });
apiLogger.info(infoToLog);
let message = 'Unauthorized'; let message = 'Unauthorized';

View File

@ -23,7 +23,6 @@
"@documenso/email": "*", "@documenso/email": "*",
"@documenso/prisma": "*", "@documenso/prisma": "*",
"@documenso/signing": "*", "@documenso/signing": "*",
"@honeybadger-io/js": "^6.10.1",
"@lingui/core": "^5.2.0", "@lingui/core": "^5.2.0",
"@lingui/macro": "^5.2.0", "@lingui/macro": "^5.2.0",
"@lingui/react": "^5.2.0", "@lingui/react": "^5.2.0",

View File

@ -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<RootApiLog> & {
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;
};

View File

@ -1,5 +1,7 @@
import { z } from 'zod'; import { z } from 'zod';
import { getIpAddress } from './get-ip-address';
const ZIpSchema = z.string().ip(); const ZIpSchema = z.string().ip();
export const ZRequestMetadataSchema = z.object({ export const ZRequestMetadataSchema = z.object({
@ -40,11 +42,13 @@ export type ApiRequestMetadata = {
}; };
export const extractRequestMetadata = (req: Request): RequestMetadata => { export const extractRequestMetadata = (req: Request): RequestMetadata => {
const forwardedFor = req.headers.get('x-forwarded-for'); let ip: string | undefined = undefined;
const ip = forwardedFor
?.split(',') try {
.map((ip) => ip.trim()) ip = getIpAddress(req);
.at(0); } catch {
// Do nothing.
}
const parsedIp = ZIpSchema.safeParse(ip); const parsedIp = ZIpSchema.safeParse(ip);

View File

@ -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<string, unknown>;
/**
* 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.
}
}
}

View File

@ -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')) { const transports: TransportTargetOptions[] = [];
// transports.push({
// target: 'pino-pretty',
// level: 'info',
// });
// }
// 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) { const loggingFilePath = env('NEXT_PRIVATE_LOGGER_FILE_PATH');
// transports.push({
// target: 'pino/file', if (loggingFilePath) {
// level: 'info', transports.push({
// options: { target: 'pino/file',
// destination: loggingFilePath, level: 'info',
// mkdir: true, options: {
// }, destination: loggingFilePath,
// }); mkdir: true,
// } },
});
}
export const logger = pino({ export const logger = pino({
level: 'info', level: 'info',
transport:
transports.length > 0
? {
targets: transports,
}
: undefined,
}); });

View File

@ -12,9 +12,15 @@ import {
export const createAdminOrganisationRoute = adminProcedure export const createAdminOrganisationRoute = adminProcedure
.input(ZCreateAdminOrganisationRequestSchema) .input(ZCreateAdminOrganisationRequestSchema)
.output(ZCreateAdminOrganisationResponseSchema) .output(ZCreateAdminOrganisationResponseSchema)
.mutation(async ({ input }) => { .mutation(async ({ input, ctx }) => {
const { ownerUserId, data } = input; const { ownerUserId, data } = input;
ctx.logger.info({
input: {
ownerUserId,
},
});
const organisation = await createOrganisation({ const organisation = await createOrganisation({
userId: ownerUserId, userId: ownerUserId,
name: data.name, name: data.name,

View File

@ -11,9 +11,15 @@ import {
export const createStripeCustomerRoute = adminProcedure export const createStripeCustomerRoute = adminProcedure
.input(ZCreateStripeCustomerRequestSchema) .input(ZCreateStripeCustomerRequestSchema)
.output(ZCreateStripeCustomerResponseSchema) .output(ZCreateStripeCustomerResponseSchema)
.mutation(async ({ input }) => { .mutation(async ({ input, ctx }) => {
const { organisationId } = input; const { organisationId } = input;
ctx.logger.info({
input: {
organisationId,
},
});
const organisation = await prisma.organisation.findUnique({ const organisation = await prisma.organisation.findUnique({
where: { where: {
id: organisationId, id: organisationId,

View File

@ -9,9 +9,13 @@ import {
export const createSubscriptionClaimRoute = adminProcedure export const createSubscriptionClaimRoute = adminProcedure
.input(ZCreateSubscriptionClaimRequestSchema) .input(ZCreateSubscriptionClaimRequestSchema)
.output(ZCreateSubscriptionClaimResponseSchema) .output(ZCreateSubscriptionClaimResponseSchema)
.mutation(async ({ input }) => { .mutation(async ({ input, ctx }) => {
const { name, teamCount, memberCount, flags } = input; const { name, teamCount, memberCount, flags } = input;
ctx.logger.info({
input,
});
await prisma.subscriptionClaim.create({ await prisma.subscriptionClaim.create({
data: { data: {
name, name,

View File

@ -10,9 +10,15 @@ import {
export const deleteSubscriptionClaimRoute = adminProcedure export const deleteSubscriptionClaimRoute = adminProcedure
.input(ZDeleteSubscriptionClaimRequestSchema) .input(ZDeleteSubscriptionClaimRequestSchema)
.output(ZDeleteSubscriptionClaimResponseSchema) .output(ZDeleteSubscriptionClaimResponseSchema)
.mutation(async ({ input }) => { .mutation(async ({ input, ctx }) => {
const { id } = input; const { id } = input;
ctx.logger.info({
input: {
id,
},
});
const existingClaim = await prisma.subscriptionClaim.findFirst({ const existingClaim = await prisma.subscriptionClaim.findFirst({
where: { where: {
id, id,

View File

@ -10,9 +10,15 @@ import {
export const getAdminOrganisationRoute = adminProcedure export const getAdminOrganisationRoute = adminProcedure
.input(ZGetAdminOrganisationRequestSchema) .input(ZGetAdminOrganisationRequestSchema)
.output(ZGetAdminOrganisationResponseSchema) .output(ZGetAdminOrganisationResponseSchema)
.query(async ({ input }) => { .query(async ({ input, ctx }) => {
const { organisationId } = input; const { organisationId } = input;
ctx.logger.info({
input: {
organisationId,
},
});
return await getAdminOrganisation({ return await getAdminOrganisation({
organisationId, organisationId,
}); });

View File

@ -61,17 +61,30 @@ export const adminRouter = router({
updateUser: adminProcedure updateUser: adminProcedure
.input(ZAdminUpdateProfileMutationSchema) .input(ZAdminUpdateProfileMutationSchema)
.mutation(async ({ input }) => { .mutation(async ({ input, ctx }) => {
const { id, name, email, roles } = input; const { id, name, email, roles } = input;
ctx.logger.info({
input: {
id,
roles,
},
});
return await updateUser({ id, name, email, roles }); return await updateUser({ id, name, email, roles });
}), }),
updateRecipient: adminProcedure updateRecipient: adminProcedure
.input(ZAdminUpdateRecipientMutationSchema) .input(ZAdminUpdateRecipientMutationSchema)
.mutation(async ({ input }) => { .mutation(async ({ input, ctx }) => {
const { id, name, email } = input; const { id, name, email } = input;
ctx.logger.info({
input: {
id,
},
});
return await updateRecipient({ id, name, email }); return await updateRecipient({ id, name, email });
}), }),
@ -80,6 +93,12 @@ export const adminRouter = router({
.mutation(async ({ ctx, input }) => { .mutation(async ({ ctx, input }) => {
const { id, enabled, data } = input; const { id, enabled, data } = input;
ctx.logger.info({
input: {
id,
},
});
return await upsertSiteSetting({ return await upsertSiteSetting({
id, id,
enabled, enabled,
@ -90,9 +109,15 @@ export const adminRouter = router({
resealDocument: adminProcedure resealDocument: adminProcedure
.input(ZAdminResealDocumentMutationSchema) .input(ZAdminResealDocumentMutationSchema)
.mutation(async ({ input }) => { .mutation(async ({ input, ctx }) => {
const { id } = input; const { id } = input;
ctx.logger.info({
input: {
id,
},
});
const document = await getEntireDocument({ id }); const document = await getEntireDocument({ id });
const isResealing = isDocumentCompleted(document.status); const isResealing = isDocumentCompleted(document.status);
@ -100,44 +125,75 @@ export const adminRouter = router({
return await sealDocument({ documentId: id, isResealing }); return await sealDocument({ documentId: id, isResealing });
}), }),
enableUser: adminProcedure.input(ZAdminEnableUserMutationSchema).mutation(async ({ input }) => { enableUser: adminProcedure
const { id } = input; .input(ZAdminEnableUserMutationSchema)
.mutation(async ({ input, ctx }) => {
const { id } = input;
const user = await getUserById({ id }).catch(() => null); ctx.logger.info({
input: {
if (!user) { id,
throw new AppError(AppErrorCode.NOT_FOUND, { },
message: 'User not found',
}); });
}
return await enableUser({ id }); const user = await getUserById({ id }).catch(() => null);
}),
disableUser: adminProcedure.input(ZAdminDisableUserMutationSchema).mutation(async ({ input }) => { if (!user) {
const { id } = input; throw new AppError(AppErrorCode.NOT_FOUND, {
message: 'User not found',
});
}
const user = await getUserById({ id }).catch(() => null); return await enableUser({ id });
}),
if (!user) { disableUser: adminProcedure
throw new AppError(AppErrorCode.NOT_FOUND, { .input(ZAdminDisableUserMutationSchema)
message: 'User not found', .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 }) => { if (!user) {
const { id } = input; 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 deleteDocument: adminProcedure
.input(ZAdminDeleteDocumentMutationSchema) .input(ZAdminDeleteDocumentMutationSchema)
.mutation(async ({ ctx, input }) => { .mutation(async ({ ctx, input }) => {
const { id, reason } = input; const { id, reason } = input;
ctx.logger.info({
input: {
id,
},
});
await sendDeleteEmail({ documentId: id, reason }); await sendDeleteEmail({ documentId: id, reason });
return await superDeleteDocument({ return await superDeleteDocument({

View File

@ -10,9 +10,15 @@ import {
export const updateAdminOrganisationRoute = adminProcedure export const updateAdminOrganisationRoute = adminProcedure
.input(ZUpdateAdminOrganisationRequestSchema) .input(ZUpdateAdminOrganisationRequestSchema)
.output(ZUpdateAdminOrganisationResponseSchema) .output(ZUpdateAdminOrganisationResponseSchema)
.mutation(async ({ input }) => { .mutation(async ({ input, ctx }) => {
const { organisationId, data } = input; const { organisationId, data } = input;
ctx.logger.info({
input: {
organisationId,
},
});
const organisation = await prisma.organisation.findUnique({ const organisation = await prisma.organisation.findUnique({
where: { where: {
id: organisationId, id: organisationId,

View File

@ -12,9 +12,13 @@ import {
export const updateSubscriptionClaimRoute = adminProcedure export const updateSubscriptionClaimRoute = adminProcedure
.input(ZUpdateSubscriptionClaimRequestSchema) .input(ZUpdateSubscriptionClaimRequestSchema)
.output(ZUpdateSubscriptionClaimResponseSchema) .output(ZUpdateSubscriptionClaimResponseSchema)
.mutation(async ({ input }) => { .mutation(async ({ input, ctx }) => {
const { id, data } = input; const { id, data } = input;
ctx.logger.info({
input,
});
const existingClaim = await prisma.subscriptionClaim.findUnique({ const existingClaim = await prisma.subscriptionClaim.findUnique({
where: { id }, where: { id },
}); });

View File

@ -15,6 +15,12 @@ export const apiTokenRouter = router({
.mutation(async ({ input, ctx }) => { .mutation(async ({ input, ctx }) => {
const { tokenName, teamId, expirationDate } = input; const { tokenName, teamId, expirationDate } = input;
ctx.logger.info({
input: {
teamId,
},
});
return await createApiToken({ return await createApiToken({
userId: ctx.user.id, userId: ctx.user.id,
teamId, teamId,
@ -28,6 +34,13 @@ export const apiTokenRouter = router({
.mutation(async ({ input, ctx }) => { .mutation(async ({ input, ctx }) => {
const { id, teamId } = input; const { id, teamId } = input;
ctx.logger.info({
input: {
id,
teamId,
},
});
return await deleteTokenById({ return await deleteTokenById({
id, id,
teamId, teamId,

View File

@ -66,6 +66,12 @@ export const authRouter = router({
.mutation(async ({ ctx, input }) => { .mutation(async ({ ctx, input }) => {
const { passkeyId } = input; const { passkeyId } = input;
ctx.logger.info({
input: {
passkeyId,
},
});
await deletePasskey({ await deletePasskey({
userId: ctx.user.id, userId: ctx.user.id,
passkeyId, passkeyId,
@ -91,6 +97,12 @@ export const authRouter = router({
.mutation(async ({ ctx, input }) => { .mutation(async ({ ctx, input }) => {
const { passkeyId, name } = input; const { passkeyId, name } = input;
ctx.logger.info({
input: {
passkeyId,
},
});
await updatePasskey({ await updatePasskey({
userId: ctx.user.id, userId: ctx.user.id,
passkeyId, passkeyId,

View File

@ -14,6 +14,13 @@ export const createSubscriptionRoute = authenticatedProcedure
.mutation(async ({ ctx, input }) => { .mutation(async ({ ctx, input }) => {
const { organisationId, priceId } = input; const { organisationId, priceId } = input;
ctx.logger.info({
input: {
organisationId,
priceId,
},
});
const userId = ctx.user.id; const userId = ctx.user.id;
if (!IS_BILLING_ENABLED()) { if (!IS_BILLING_ENABLED()) {

View File

@ -13,6 +13,12 @@ export const getInvoicesRoute = authenticatedProcedure
.query(async ({ ctx, input }) => { .query(async ({ ctx, input }) => {
const { organisationId } = input; const { organisationId } = input;
ctx.logger.info({
input: {
organisationId,
},
});
const userId = ctx.user.id; const userId = ctx.user.id;
if (!IS_BILLING_ENABLED()) { if (!IS_BILLING_ENABLED()) {

View File

@ -11,6 +11,12 @@ export const getSubscriptionRoute = authenticatedProcedure
.query(async ({ ctx, input }) => { .query(async ({ ctx, input }) => {
const { organisationId } = input; const { organisationId } = input;
ctx.logger.info({
input: {
organisationId,
},
});
const userId = ctx.user.id; const userId = ctx.user.id;
if (!IS_BILLING_ENABLED()) { if (!IS_BILLING_ENABLED()) {

View File

@ -14,6 +14,12 @@ export const manageSubscriptionRoute = authenticatedProcedure
.mutation(async ({ ctx, input }) => { .mutation(async ({ ctx, input }) => {
const { organisationId } = input; const { organisationId } = input;
ctx.logger.info({
input: {
organisationId,
},
});
const userId = ctx.user.id; const userId = ctx.user.id;
if (!IS_BILLING_ENABLED()) { if (!IS_BILLING_ENABLED()) {

View File

@ -5,8 +5,10 @@ import { z } from 'zod';
import type { SessionUser } from '@documenso/auth/server/lib/session/session'; import type { SessionUser } from '@documenso/auth/server/lib/session/session';
import { getOptionalSession } from '@documenso/auth/server/lib/utils/get-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 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 // This is a bit nasty. Todo: Extract
import type { HonoEnv } from '@documenso/remix/server/router'; import type { HonoEnv } from '@documenso/remix/server/router';
@ -22,16 +24,23 @@ export const createTrpcContext = async ({
const { session, user } = await getOptionalSession(c); const { session, user } = await getOptionalSession(c);
const req = c.req.raw; const req = c.req.raw;
const logger = c.get('logger');
const requestMetadata = c.get('context').requestMetadata;
const metadata: ApiRequestMetadata = { const metadata: ApiRequestMetadata = {
requestMetadata: extractRequestMetadata(req), requestMetadata,
source: requestSource, source: requestSource,
auth: null, auth: null,
}; };
const rawTeamId = req.headers.get('x-team-id') || undefined; 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 const teamId = z.coerce
.number() .number()
.optional() .optional()
@ -40,7 +49,7 @@ export const createTrpcContext = async ({
if (!session || !user) { if (!session || !user) {
return { return {
logger, logger: trpcLogger,
session: null, session: null,
user: null, user: null,
teamId, teamId,
@ -50,7 +59,7 @@ export const createTrpcContext = async ({
} }
return { return {
logger, logger: trpcLogger,
session, session,
user, user,
teamId, teamId,

View File

@ -62,6 +62,7 @@ export const documentRouter = router({
find: findInboxRoute, find: findInboxRoute,
getCount: getInboxCountRoute, getCount: getInboxCountRoute,
}, },
updateDocument: updateDocumentRoute,
/** /**
* @private * @private
@ -72,6 +73,12 @@ export const documentRouter = router({
const { teamId } = ctx; const { teamId } = ctx;
const { documentId } = input; const { documentId } = input;
ctx.logger.info({
input: {
documentId,
},
});
return await getDocumentById({ return await getDocumentById({
userId: ctx.user.id, userId: ctx.user.id,
teamId, teamId,
@ -231,6 +238,13 @@ export const documentRouter = router({
const { teamId, user } = ctx; const { teamId, user } = ctx;
const { documentId, folderId } = input; const { documentId, folderId } = input;
ctx.logger.info({
input: {
documentId,
folderId,
},
});
return await getDocumentWithDetailsById({ return await getDocumentWithDetailsById({
userId: user.id, userId: user.id,
teamId, teamId,
@ -332,6 +346,12 @@ export const documentRouter = router({
const { user, teamId } = ctx; const { user, teamId } = ctx;
const { title, documentDataId, timezone, folderId } = input; const { title, documentDataId, timezone, folderId } = input;
ctx.logger.info({
input: {
folderId,
},
});
const { remaining } = await getServerLimits({ userId: user.id, teamId }); const { remaining } = await getServerLimits({ userId: user.id, teamId });
if (remaining.documents <= 0) { if (remaining.documents <= 0) {
@ -353,8 +373,6 @@ export const documentRouter = router({
}); });
}), }),
updateDocument: updateDocumentRoute,
/** /**
* @public * @public
*/ */
@ -373,6 +391,12 @@ export const documentRouter = router({
const { teamId } = ctx; const { teamId } = ctx;
const { documentId } = input; const { documentId } = input;
ctx.logger.info({
input: {
documentId,
},
});
const userId = ctx.user.id; const userId = ctx.user.id;
await deleteDocument({ await deleteDocument({
@ -396,6 +420,13 @@ export const documentRouter = router({
const { teamId } = ctx; const { teamId } = ctx;
const { documentId, signingOrder } = input; const { documentId, signingOrder } = input;
ctx.logger.info({
input: {
documentId,
signingOrder,
},
});
return await upsertDocumentMeta({ return await upsertDocumentMeta({
userId: ctx.user.id, userId: ctx.user.id,
teamId, teamId,
@ -427,6 +458,12 @@ export const documentRouter = router({
const { teamId } = ctx; const { teamId } = ctx;
const { documentId, meta = {} } = input; const { documentId, meta = {} } = input;
ctx.logger.info({
input: {
documentId,
},
});
if (Object.values(meta).length > 0) { if (Object.values(meta).length > 0) {
await upsertDocumentMeta({ await upsertDocumentMeta({
userId: ctx.user.id, userId: ctx.user.id,
@ -474,6 +511,13 @@ export const documentRouter = router({
const { teamId } = ctx; const { teamId } = ctx;
const { documentId, recipients } = input; const { documentId, recipients } = input;
ctx.logger.info({
input: {
documentId,
recipients,
},
});
await resendDocument({ await resendDocument({
userId: ctx.user.id, userId: ctx.user.id,
teamId, teamId,
@ -503,6 +547,12 @@ export const documentRouter = router({
const { teamId, user } = ctx; const { teamId, user } = ctx;
const { documentId } = input; const { documentId } = input;
ctx.logger.info({
input: {
documentId,
},
});
return await duplicateDocument({ return await duplicateDocument({
userId: user.id, userId: user.id,
teamId, teamId,
@ -544,6 +594,12 @@ export const documentRouter = router({
orderByDirection, orderByDirection,
} = input; } = input;
ctx.logger.info({
input: {
documentId,
},
});
return await findDocumentAuditLogs({ return await findDocumentAuditLogs({
userId: ctx.user.id, userId: ctx.user.id,
teamId, teamId,
@ -565,6 +621,12 @@ export const documentRouter = router({
const { teamId } = ctx; const { teamId } = ctx;
const { documentId } = input; const { documentId } = input;
ctx.logger.info({
input: {
documentId,
},
});
const document = await getDocumentById({ const document = await getDocumentById({
documentId, documentId,
userId: ctx.user.id, userId: ctx.user.id,
@ -597,6 +659,12 @@ export const documentRouter = router({
const { teamId } = ctx; const { teamId } = ctx;
const { documentId } = input; const { documentId } = input;
ctx.logger.info({
input: {
documentId,
},
});
const document = await getDocumentById({ const document = await getDocumentById({
documentId, documentId,
userId: ctx.user.id, userId: ctx.user.id,

View File

@ -19,6 +19,12 @@ export const updateDocumentRoute = authenticatedProcedure
const { teamId } = ctx; const { teamId } = ctx;
const { documentId, data, meta = {} } = input; const { documentId, data, meta = {} } = input;
ctx.logger.info({
input: {
documentId,
},
});
const userId = ctx.user.id; const userId = ctx.user.id;
if (Object.values(meta).length > 0) { if (Object.values(meta).length > 0) {

View File

@ -16,6 +16,12 @@ export const updateEmbeddingDocumentRoute = procedure
.input(ZUpdateEmbeddingDocumentRequestSchema) .input(ZUpdateEmbeddingDocumentRequestSchema)
.output(ZUpdateEmbeddingDocumentResponseSchema) .output(ZUpdateEmbeddingDocumentResponseSchema)
.mutation(async ({ input, ctx }) => { .mutation(async ({ input, ctx }) => {
ctx.logger.info({
input: {
documentId: input.documentId,
},
});
try { try {
const authorizationHeader = ctx.req.headers.get('authorization'); const authorizationHeader = ctx.req.headers.get('authorization');

View File

@ -15,6 +15,12 @@ export const updateEmbeddingTemplateRoute = procedure
.input(ZUpdateEmbeddingTemplateRequestSchema) .input(ZUpdateEmbeddingTemplateRequestSchema)
.output(ZUpdateEmbeddingTemplateResponseSchema) .output(ZUpdateEmbeddingTemplateResponseSchema)
.mutation(async ({ input, ctx }) => { .mutation(async ({ input, ctx }) => {
ctx.logger.info({
input: {
templateId: input.templateId,
},
});
try { try {
const authorizationHeader = ctx.req.headers.get('authorization'); const authorizationHeader = ctx.req.headers.get('authorization');

View File

@ -62,6 +62,12 @@ export const fieldRouter = router({
const { teamId } = ctx; const { teamId } = ctx;
const { fieldId } = input; const { fieldId } = input;
ctx.logger.info({
input: {
fieldId,
},
});
return await getFieldById({ return await getFieldById({
userId: ctx.user.id, userId: ctx.user.id,
teamId, teamId,
@ -88,6 +94,12 @@ export const fieldRouter = router({
const { teamId } = ctx; const { teamId } = ctx;
const { documentId, field } = input; const { documentId, field } = input;
ctx.logger.info({
input: {
documentId,
},
});
const createdFields = await createDocumentFields({ const createdFields = await createDocumentFields({
userId: ctx.user.id, userId: ctx.user.id,
teamId, teamId,
@ -118,6 +130,12 @@ export const fieldRouter = router({
const { teamId } = ctx; const { teamId } = ctx;
const { documentId, fields } = input; const { documentId, fields } = input;
ctx.logger.info({
input: {
documentId,
},
});
return await createDocumentFields({ return await createDocumentFields({
userId: ctx.user.id, userId: ctx.user.id,
teamId, teamId,
@ -146,6 +164,12 @@ export const fieldRouter = router({
const { teamId } = ctx; const { teamId } = ctx;
const { documentId, field } = input; const { documentId, field } = input;
ctx.logger.info({
input: {
documentId,
},
});
const updatedFields = await updateDocumentFields({ const updatedFields = await updateDocumentFields({
userId: ctx.user.id, userId: ctx.user.id,
teamId, teamId,
@ -176,6 +200,12 @@ export const fieldRouter = router({
const { teamId } = ctx; const { teamId } = ctx;
const { documentId, fields } = input; const { documentId, fields } = input;
ctx.logger.info({
input: {
documentId,
},
});
return await updateDocumentFields({ return await updateDocumentFields({
userId: ctx.user.id, userId: ctx.user.id,
teamId, teamId,
@ -203,6 +233,12 @@ export const fieldRouter = router({
const { teamId } = ctx; const { teamId } = ctx;
const { fieldId } = input; const { fieldId } = input;
ctx.logger.info({
input: {
fieldId,
},
});
await deleteDocumentField({ await deleteDocumentField({
userId: ctx.user.id, userId: ctx.user.id,
teamId, teamId,
@ -225,6 +261,12 @@ export const fieldRouter = router({
const { teamId } = ctx; const { teamId } = ctx;
const { documentId, fields } = input; const { documentId, fields } = input;
ctx.logger.info({
input: {
documentId,
},
});
return await setFieldsForDocument({ return await setFieldsForDocument({
documentId, documentId,
userId: ctx.user.id, userId: ctx.user.id,
@ -263,6 +305,12 @@ export const fieldRouter = router({
const { teamId } = ctx; const { teamId } = ctx;
const { templateId, field } = input; const { templateId, field } = input;
ctx.logger.info({
input: {
templateId,
},
});
const createdFields = await createTemplateFields({ const createdFields = await createTemplateFields({
userId: ctx.user.id, userId: ctx.user.id,
teamId, teamId,
@ -293,6 +341,12 @@ export const fieldRouter = router({
const { teamId } = ctx; const { teamId } = ctx;
const { fieldId } = input; const { fieldId } = input;
ctx.logger.info({
input: {
fieldId,
},
});
return await getFieldById({ return await getFieldById({
userId: ctx.user.id, userId: ctx.user.id,
teamId, teamId,
@ -319,6 +373,12 @@ export const fieldRouter = router({
const { teamId } = ctx; const { teamId } = ctx;
const { templateId, fields } = input; const { templateId, fields } = input;
ctx.logger.info({
input: {
templateId,
},
});
return await createTemplateFields({ return await createTemplateFields({
userId: ctx.user.id, userId: ctx.user.id,
teamId, teamId,
@ -346,6 +406,12 @@ export const fieldRouter = router({
const { teamId } = ctx; const { teamId } = ctx;
const { templateId, field } = input; const { templateId, field } = input;
ctx.logger.info({
input: {
templateId,
},
});
const updatedFields = await updateTemplateFields({ const updatedFields = await updateTemplateFields({
userId: ctx.user.id, userId: ctx.user.id,
teamId, teamId,
@ -375,6 +441,12 @@ export const fieldRouter = router({
const { teamId } = ctx; const { teamId } = ctx;
const { templateId, fields } = input; const { templateId, fields } = input;
ctx.logger.info({
input: {
templateId,
},
});
return await updateTemplateFields({ return await updateTemplateFields({
userId: ctx.user.id, userId: ctx.user.id,
teamId, teamId,
@ -401,6 +473,12 @@ export const fieldRouter = router({
const { teamId } = ctx; const { teamId } = ctx;
const { fieldId } = input; const { fieldId } = input;
ctx.logger.info({
input: {
fieldId,
},
});
await deleteTemplateField({ await deleteTemplateField({
userId: ctx.user.id, userId: ctx.user.id,
teamId, teamId,
@ -422,6 +500,12 @@ export const fieldRouter = router({
const { teamId } = ctx; const { teamId } = ctx;
const { templateId, fields } = input; const { templateId, fields } = input;
ctx.logger.info({
input: {
templateId,
},
});
return await setFieldsForTemplate({ return await setFieldsForTemplate({
templateId, templateId,
userId: ctx.user.id, userId: ctx.user.id,
@ -448,6 +532,12 @@ export const fieldRouter = router({
.mutation(async ({ input, ctx }) => { .mutation(async ({ input, ctx }) => {
const { token, fieldId, value, isBase64, authOptions } = input; const { token, fieldId, value, isBase64, authOptions } = input;
ctx.logger.info({
input: {
fieldId,
},
});
return await signFieldWithToken({ return await signFieldWithToken({
token, token,
fieldId, fieldId,
@ -467,6 +557,12 @@ export const fieldRouter = router({
.mutation(async ({ input, ctx }) => { .mutation(async ({ input, ctx }) => {
const { token, fieldId } = input; const { token, fieldId } = input;
ctx.logger.info({
input: {
fieldId,
},
});
return await removeSignedFieldWithToken({ return await removeSignedFieldWithToken({
token, token,
fieldId, fieldId,

View File

@ -42,6 +42,13 @@ export const folderRouter = router({
const { teamId, user } = ctx; const { teamId, user } = ctx;
const { parentId, type } = input; const { parentId, type } = input;
ctx.logger.info({
input: {
parentId,
type,
},
});
const folders = await findFolders({ const folders = await findFolders({
userId: user.id, userId: user.id,
teamId, teamId,
@ -75,6 +82,13 @@ export const folderRouter = router({
const { teamId, user } = ctx; const { teamId, user } = ctx;
const { parentId, type } = input; const { parentId, type } = input;
ctx.logger.info({
input: {
parentId,
type,
},
});
const folders = await findFolders({ const folders = await findFolders({
userId: user.id, userId: user.id,
teamId, teamId,
@ -107,6 +121,13 @@ export const folderRouter = router({
const { teamId, user } = ctx; const { teamId, user } = ctx;
const { name, parentId, type } = input; const { name, parentId, type } = input;
ctx.logger.info({
input: {
parentId,
type,
},
});
if (parentId) { if (parentId) {
try { try {
await getFolderById({ await getFolderById({
@ -146,6 +167,12 @@ export const folderRouter = router({
const { teamId, user } = ctx; const { teamId, user } = ctx;
const { id, name, visibility } = input; const { id, name, visibility } = input;
ctx.logger.info({
input: {
id,
},
});
const currentFolder = await getFolderById({ const currentFolder = await getFolderById({
userId: user.id, userId: user.id,
teamId, teamId,
@ -177,6 +204,12 @@ export const folderRouter = router({
const { teamId, user } = ctx; const { teamId, user } = ctx;
const { id } = input; const { id } = input;
ctx.logger.info({
input: {
id,
},
});
await deleteFolder({ await deleteFolder({
userId: user.id, userId: user.id,
teamId, teamId,
@ -193,6 +226,13 @@ export const folderRouter = router({
const { teamId, user } = ctx; const { teamId, user } = ctx;
const { id, parentId } = input; const { id, parentId } = input;
ctx.logger.info({
input: {
id,
parentId,
},
});
const currentFolder = await getFolderById({ const currentFolder = await getFolderById({
userId: user.id, userId: user.id,
teamId, teamId,
@ -238,6 +278,13 @@ export const folderRouter = router({
const { teamId, user } = ctx; const { teamId, user } = ctx;
const { documentId, folderId } = input; const { documentId, folderId } = input;
ctx.logger.info({
input: {
documentId,
folderId,
},
});
if (folderId !== null) { if (folderId !== null) {
try { try {
await getFolderById({ await getFolderById({
@ -277,6 +324,13 @@ export const folderRouter = router({
const { teamId, user } = ctx; const { teamId, user } = ctx;
const { templateId, folderId } = input; const { templateId, folderId } = input;
ctx.logger.info({
input: {
templateId,
folderId,
},
});
if (folderId !== null) { if (folderId !== null) {
try { try {
await getFolderById({ await getFolderById({
@ -310,16 +364,24 @@ export const folderRouter = router({
* @private * @private
*/ */
pinFolder: authenticatedProcedure.input(ZPinFolderSchema).mutation(async ({ ctx, input }) => { pinFolder: authenticatedProcedure.input(ZPinFolderSchema).mutation(async ({ ctx, input }) => {
const { folderId } = input;
ctx.logger.info({
input: {
folderId,
},
});
const currentFolder = await getFolderById({ const currentFolder = await getFolderById({
userId: ctx.user.id, userId: ctx.user.id,
teamId: ctx.teamId, teamId: ctx.teamId,
folderId: input.folderId, folderId,
}); });
const result = await pinFolder({ const result = await pinFolder({
userId: ctx.user.id, userId: ctx.user.id,
teamId: ctx.teamId, teamId: ctx.teamId,
folderId: input.folderId, folderId,
type: currentFolder.type, type: currentFolder.type,
}); });
@ -333,16 +395,24 @@ export const folderRouter = router({
* @private * @private
*/ */
unpinFolder: authenticatedProcedure.input(ZUnpinFolderSchema).mutation(async ({ ctx, input }) => { unpinFolder: authenticatedProcedure.input(ZUnpinFolderSchema).mutation(async ({ ctx, input }) => {
const { folderId } = input;
ctx.logger.info({
input: {
folderId,
},
});
const currentFolder = await getFolderById({ const currentFolder = await getFolderById({
userId: ctx.user.id, userId: ctx.user.id,
teamId: ctx.teamId, teamId: ctx.teamId,
folderId: input.folderId, folderId,
}); });
const result = await unpinFolder({ const result = await unpinFolder({
userId: ctx.user.id, userId: ctx.user.id,
teamId: ctx.teamId, teamId: ctx.teamId,
folderId: input.folderId, folderId,
type: currentFolder.type, type: currentFolder.type,
}); });

View File

@ -24,6 +24,12 @@ export const createOrganisationGroupRoute = authenticatedProcedure
const { organisationId, organisationRole, name, memberIds } = input; const { organisationId, organisationRole, name, memberIds } = input;
const { user } = ctx; const { user } = ctx;
ctx.logger.info({
input: {
organisationId,
},
});
const organisation = await prisma.organisation.findFirst({ const organisation = await prisma.organisation.findFirst({
where: buildOrganisationWhereQuery({ where: buildOrganisationWhereQuery({
organisationId, organisationId,

View File

@ -14,6 +14,12 @@ export const createOrganisationMemberInvitesRoute = authenticatedProcedure
const userId = ctx.user.id; const userId = ctx.user.id;
const userName = ctx.user.name || ''; const userName = ctx.user.name || '';
ctx.logger.info({
input: {
organisationId,
},
});
await createOrganisationMemberInvites({ await createOrganisationMemberInvites({
userId, userId,
userName, userName,

View File

@ -23,6 +23,12 @@ export const createOrganisationRoute = authenticatedProcedure
const { name, priceId } = input; const { name, priceId } = input;
const { user } = ctx; const { user } = ctx;
ctx.logger.info({
input: {
priceId,
},
});
// Check if user can create a free organiastion. // Check if user can create a free organiastion.
if (IS_BILLING_ENABLED() && !priceId) { if (IS_BILLING_ENABLED() && !priceId) {
const userOrganisations = await prisma.organisation.findMany({ const userOrganisations = await prisma.organisation.findMany({

View File

@ -19,6 +19,13 @@ export const deleteOrganisationGroupRoute = authenticatedProcedure
const { groupId, organisationId } = input; const { groupId, organisationId } = input;
const { user } = ctx; const { user } = ctx;
ctx.logger.info({
input: {
groupId,
organisationId,
},
});
const organisation = await prisma.organisation.findFirst({ const organisation = await prisma.organisation.findFirst({
where: buildOrganisationWhereQuery({ where: buildOrganisationWhereQuery({
organisationId, organisationId,

View File

@ -19,6 +19,13 @@ export const deleteOrganisationMemberInvitesRoute = authenticatedProcedure
const { organisationId, invitationIds } = input; const { organisationId, invitationIds } = input;
const userId = ctx.user.id; const userId = ctx.user.id;
ctx.logger.info({
input: {
organisationId,
invitationIds,
},
});
const organisation = await prisma.organisation.findFirst({ const organisation = await prisma.organisation.findFirst({
where: buildOrganisationWhereQuery({ where: buildOrganisationWhereQuery({
organisationId, organisationId,

View File

@ -13,6 +13,13 @@ export const deleteOrganisationMemberRoute = authenticatedProcedure
const { organisationId, organisationMemberId } = input; const { organisationId, organisationMemberId } = input;
const userId = ctx.user.id; const userId = ctx.user.id;
ctx.logger.info({
input: {
organisationId,
organisationMemberId,
},
});
await deleteOrganisationMembers({ await deleteOrganisationMembers({
userId, userId,
organisationId, organisationId,

View File

@ -21,6 +21,13 @@ export const deleteOrganisationMembersRoute = authenticatedProcedure
const { organisationId, organisationMemberIds } = input; const { organisationId, organisationMemberIds } = input;
const userId = ctx.user.id; const userId = ctx.user.id;
ctx.logger.info({
input: {
organisationId,
organisationMemberIds,
},
});
await deleteOrganisationMembers({ await deleteOrganisationMembers({
userId, userId,
organisationId, organisationId,

View File

@ -17,6 +17,12 @@ export const deleteOrganisationRoute = authenticatedProcedure
const { organisationId } = input; const { organisationId } = input;
const { user } = ctx; const { user } = ctx;
ctx.logger.info({
input: {
organisationId,
},
});
const organisation = await prisma.organisation.findFirst({ const organisation = await prisma.organisation.findFirst({
where: buildOrganisationWhereQuery({ where: buildOrganisationWhereQuery({
organisationId, organisationId,

View File

@ -21,6 +21,12 @@ export const findOrganisationGroupsRoute = authenticatedProcedure
input; input;
const { user } = ctx; const { user } = ctx;
ctx.logger.info({
input: {
organisationId,
},
});
return await findOrganisationGroups({ return await findOrganisationGroups({
userId: user.id, userId: user.id,
organisationId, organisationId,

View File

@ -21,6 +21,12 @@ export const findOrganisationMemberInvitesRoute = authenticatedProcedure
const { organisationId, query, page, perPage, status } = input; const { organisationId, query, page, perPage, status } = input;
const { user } = ctx; const { user } = ctx;
ctx.logger.info({
input: {
organisationId,
},
});
return await findOrganisationMemberInvites({ return await findOrganisationMemberInvites({
userId: user.id, userId: user.id,
organisationId, organisationId,

View File

@ -14,6 +14,12 @@ export const getOrganisationRoute = authenticatedProcedure
.query(async ({ input, ctx }) => { .query(async ({ input, ctx }) => {
const { organisationReference } = input; const { organisationReference } = input;
ctx.logger.info({
input: {
organisationReference,
},
});
return await getOrganisation({ return await getOrganisation({
userId: ctx.user.id, userId: ctx.user.id,
organisationReference, organisationReference,

View File

@ -17,9 +17,14 @@ export const leaveOrganisationRoute = authenticatedProcedure
.output(ZLeaveOrganisationResponseSchema) .output(ZLeaveOrganisationResponseSchema)
.mutation(async ({ ctx, input }) => { .mutation(async ({ ctx, input }) => {
const { organisationId } = input; const { organisationId } = input;
const userId = ctx.user.id; const userId = ctx.user.id;
ctx.logger.info({
input: {
organisationId,
},
});
const organisation = await prisma.organisation.findFirst({ const organisation = await prisma.organisation.findFirst({
where: buildOrganisationWhereQuery({ organisationId, userId }), where: buildOrganisationWhereQuery({ organisationId, userId }),
include: { include: {

View File

@ -20,6 +20,13 @@ export const resendOrganisationMemberInviteRoute = authenticatedProcedure
const userId = ctx.user.id; const userId = ctx.user.id;
const userName = ctx.user.name || ''; const userName = ctx.user.name || '';
ctx.logger.info({
input: {
organisationId,
invitationId,
},
});
await resendOrganisationMemberInvitation({ await resendOrganisationMemberInvitation({
userId, userId,
userName, userName,

View File

@ -25,6 +25,12 @@ export const updateOrganisationGroupRoute = authenticatedProcedure
const { id, ...data } = input; const { id, ...data } = input;
const { user } = ctx; const { user } = ctx;
ctx.logger.info({
input: {
id,
},
});
const organisationGroup = await prisma.organisationGroup.findFirst({ const organisationGroup = await prisma.organisationGroup.findFirst({
where: { where: {
id, id,

View File

@ -24,6 +24,13 @@ export const updateOrganisationMemberRoute = authenticatedProcedure
const { organisationId, organisationMemberId, data } = input; const { organisationId, organisationMemberId, data } = input;
const userId = ctx.user.id; const userId = ctx.user.id;
ctx.logger.info({
input: {
organisationId,
organisationMemberId,
},
});
const organisation = await prisma.organisation.findFirst({ const organisation = await prisma.organisation.findFirst({
where: buildOrganisationWhereQuery({ where: buildOrganisationWhereQuery({
organisationId, organisationId,

View File

@ -16,6 +16,12 @@ export const updateOrganisationSettingsRoute = authenticatedProcedure
const { user } = ctx; const { user } = ctx;
const { organisationId, data } = input; const { organisationId, data } = input;
ctx.logger.info({
input: {
organisationId,
},
});
const { const {
// Document related settings. // Document related settings.
documentVisibility, documentVisibility,

View File

@ -15,9 +15,14 @@ export const updateOrganisationRoute = authenticatedProcedure
.output(ZUpdateOrganisationResponseSchema) .output(ZUpdateOrganisationResponseSchema)
.mutation(async ({ input, ctx }) => { .mutation(async ({ input, ctx }) => {
const { organisationId, data } = input; const { organisationId, data } = input;
const userId = ctx.user.id; const userId = ctx.user.id;
ctx.logger.info({
input: {
organisationId,
},
});
// Check if organisation exists and user has access to it // Check if organisation exists and user has access to it
const existingOrganisation = await prisma.organisation.findFirst({ const existingOrganisation = await prisma.organisation.findFirst({
where: buildOrganisationWhereQuery({ where: buildOrganisationWhereQuery({

View File

@ -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; const { id } = input;
ctx.logger.info({
input: {
id,
},
});
return await getUserById({ id }); return await getUserById({ id });
}), }),
@ -53,6 +59,13 @@ export const profileRouter = router({
.mutation(async ({ input, ctx }) => { .mutation(async ({ input, ctx }) => {
const { bytes, teamId, organisationId } = input; const { bytes, teamId, organisationId } = input;
ctx.logger.info({
input: {
teamId,
organisationId,
},
});
let target: SetAvatarImageOptions['target'] = { let target: SetAvatarImageOptions['target'] = {
type: 'user', type: 'user',
}; };

View File

@ -62,6 +62,12 @@ export const recipientRouter = router({
const { teamId } = ctx; const { teamId } = ctx;
const { recipientId } = input; const { recipientId } = input;
ctx.logger.info({
input: {
recipientId,
},
});
return await getRecipientById({ return await getRecipientById({
userId: ctx.user.id, userId: ctx.user.id,
teamId, teamId,
@ -88,6 +94,12 @@ export const recipientRouter = router({
const { teamId } = ctx; const { teamId } = ctx;
const { documentId, recipient } = input; const { documentId, recipient } = input;
ctx.logger.info({
input: {
documentId,
},
});
const createdRecipients = await createDocumentRecipients({ const createdRecipients = await createDocumentRecipients({
userId: ctx.user.id, userId: ctx.user.id,
teamId, teamId,
@ -118,6 +130,12 @@ export const recipientRouter = router({
const { teamId } = ctx; const { teamId } = ctx;
const { documentId, recipients } = input; const { documentId, recipients } = input;
ctx.logger.info({
input: {
documentId,
},
});
return await createDocumentRecipients({ return await createDocumentRecipients({
userId: ctx.user.id, userId: ctx.user.id,
teamId, teamId,
@ -146,6 +164,12 @@ export const recipientRouter = router({
const { teamId } = ctx; const { teamId } = ctx;
const { documentId, recipient } = input; const { documentId, recipient } = input;
ctx.logger.info({
input: {
documentId,
},
});
const updatedRecipients = await updateDocumentRecipients({ const updatedRecipients = await updateDocumentRecipients({
userId: ctx.user.id, userId: ctx.user.id,
teamId, teamId,
@ -176,6 +200,12 @@ export const recipientRouter = router({
const { teamId } = ctx; const { teamId } = ctx;
const { documentId, recipients } = input; const { documentId, recipients } = input;
ctx.logger.info({
input: {
documentId,
},
});
return await updateDocumentRecipients({ return await updateDocumentRecipients({
userId: ctx.user.id, userId: ctx.user.id,
teamId, teamId,
@ -203,6 +233,12 @@ export const recipientRouter = router({
const { teamId } = ctx; const { teamId } = ctx;
const { recipientId } = input; const { recipientId } = input;
ctx.logger.info({
input: {
recipientId,
},
});
await deleteDocumentRecipient({ await deleteDocumentRecipient({
userId: ctx.user.id, userId: ctx.user.id,
teamId, teamId,
@ -223,6 +259,12 @@ export const recipientRouter = router({
const { teamId } = ctx; const { teamId } = ctx;
const { documentId, recipients } = input; const { documentId, recipients } = input;
ctx.logger.info({
input: {
documentId,
},
});
return await setDocumentRecipients({ return await setDocumentRecipients({
userId: ctx.user.id, userId: ctx.user.id,
teamId, teamId,
@ -259,6 +301,12 @@ export const recipientRouter = router({
const { teamId } = ctx; const { teamId } = ctx;
const { recipientId } = input; const { recipientId } = input;
ctx.logger.info({
input: {
recipientId,
},
});
return await getRecipientById({ return await getRecipientById({
userId: ctx.user.id, userId: ctx.user.id,
teamId, teamId,
@ -285,6 +333,12 @@ export const recipientRouter = router({
const { teamId } = ctx; const { teamId } = ctx;
const { templateId, recipient } = input; const { templateId, recipient } = input;
ctx.logger.info({
input: {
templateId,
},
});
const createdRecipients = await createTemplateRecipients({ const createdRecipients = await createTemplateRecipients({
userId: ctx.user.id, userId: ctx.user.id,
teamId, teamId,
@ -314,6 +368,12 @@ export const recipientRouter = router({
const { teamId } = ctx; const { teamId } = ctx;
const { templateId, recipients } = input; const { templateId, recipients } = input;
ctx.logger.info({
input: {
templateId,
},
});
return await createTemplateRecipients({ return await createTemplateRecipients({
userId: ctx.user.id, userId: ctx.user.id,
teamId, teamId,
@ -341,6 +401,12 @@ export const recipientRouter = router({
const { teamId } = ctx; const { teamId } = ctx;
const { templateId, recipient } = input; const { templateId, recipient } = input;
ctx.logger.info({
input: {
templateId,
},
});
const updatedRecipients = await updateTemplateRecipients({ const updatedRecipients = await updateTemplateRecipients({
userId: ctx.user.id, userId: ctx.user.id,
teamId, teamId,
@ -370,6 +436,12 @@ export const recipientRouter = router({
const { teamId } = ctx; const { teamId } = ctx;
const { templateId, recipients } = input; const { templateId, recipients } = input;
ctx.logger.info({
input: {
templateId,
},
});
return await updateTemplateRecipients({ return await updateTemplateRecipients({
userId: ctx.user.id, userId: ctx.user.id,
teamId, teamId,
@ -396,6 +468,12 @@ export const recipientRouter = router({
const { teamId } = ctx; const { teamId } = ctx;
const { recipientId } = input; const { recipientId } = input;
ctx.logger.info({
input: {
recipientId,
},
});
await deleteTemplateRecipient({ await deleteTemplateRecipient({
recipientId, recipientId,
userId: ctx.user.id, userId: ctx.user.id,
@ -415,6 +493,12 @@ export const recipientRouter = router({
const { teamId } = ctx; const { teamId } = ctx;
const { templateId, recipients } = input; const { templateId, recipients } = input;
ctx.logger.info({
input: {
templateId,
},
});
return await setTemplateRecipients({ return await setTemplateRecipients({
userId: ctx.user.id, userId: ctx.user.id,
teamId, teamId,
@ -438,6 +522,12 @@ export const recipientRouter = router({
.mutation(async ({ input, ctx }) => { .mutation(async ({ input, ctx }) => {
const { token, documentId, authOptions, nextSigner } = input; const { token, documentId, authOptions, nextSigner } = input;
ctx.logger.info({
input: {
documentId,
},
});
return await completeDocumentWithToken({ return await completeDocumentWithToken({
token, token,
documentId, documentId,
@ -456,6 +546,12 @@ export const recipientRouter = router({
.mutation(async ({ input, ctx }) => { .mutation(async ({ input, ctx }) => {
const { token, documentId, reason } = input; const { token, documentId, reason } = input;
ctx.logger.info({
input: {
documentId,
},
});
return await rejectDocumentWithToken({ return await rejectDocumentWithToken({
token, token,
documentId, documentId,

View File

@ -14,6 +14,12 @@ export const getDocumentInternalUrlForQRCodeRoute = procedure
.query(async ({ input, ctx }) => { .query(async ({ input, ctx }) => {
const { documentId } = input; const { documentId } = input;
ctx.logger.info({
input: {
documentId,
},
});
if (!ctx.user) { if (!ctx.user) {
return null; return null;
} }

View File

@ -10,6 +10,12 @@ export const shareLinkRouter = router({
.mutation(async ({ ctx, input }) => { .mutation(async ({ ctx, input }) => {
const { documentId, token } = input; const { documentId, token } = input;
ctx.logger.info({
input: {
documentId,
},
});
if (token) { if (token) {
return await createOrGetShareLink({ documentId, token }); return await createOrGetShareLink({ documentId, token });
} }

View File

@ -27,6 +27,13 @@ export const createTeamGroupsRoute = authenticatedProcedure
const { teamId, groups } = input; const { teamId, groups } = input;
const { user } = ctx; const { user } = ctx;
ctx.logger.info({
input: {
teamId,
groups,
},
});
const team = await prisma.team.findFirst({ const team = await prisma.team.findFirst({
where: buildTeamWhereQuery({ where: buildTeamWhereQuery({
teamId, teamId,

View File

@ -21,6 +21,13 @@ export const createTeamMembersRoute = authenticatedProcedure
const { teamId, organisationMembers } = input; const { teamId, organisationMembers } = input;
const { user } = ctx; const { user } = ctx;
ctx.logger.info({
input: {
teamId,
organisationMembers,
},
});
return await createTeamMembers({ return await createTeamMembers({
userId: user.id, userId: user.id,
teamId, teamId,

View File

@ -11,6 +11,12 @@ export const createTeamRoute = authenticatedProcedure
const { teamName, teamUrl, organisationId, inheritMembers } = input; const { teamName, teamUrl, organisationId, inheritMembers } = input;
const { user } = ctx; const { user } = ctx;
ctx.logger.info({
input: {
organisationId,
},
});
return await createTeam({ return await createTeam({
userId: user.id, userId: user.id,
teamName, teamName,

View File

@ -19,6 +19,13 @@ export const deleteTeamGroupRoute = authenticatedProcedure
const { teamGroupId, teamId } = input; const { teamGroupId, teamId } = input;
const { user } = ctx; const { user } = ctx;
ctx.logger.info({
input: {
teamGroupId,
teamId,
},
});
const team = await prisma.team.findFirst({ const team = await prisma.team.findFirst({
where: buildTeamWhereQuery({ where: buildTeamWhereQuery({
teamId, teamId,

View File

@ -20,6 +20,13 @@ export const deleteTeamMemberRoute = authenticatedProcedure
const { teamId, memberId } = input; const { teamId, memberId } = input;
const { user } = ctx; const { user } = ctx;
ctx.logger.info({
input: {
teamId,
memberId,
},
});
const team = await prisma.team.findFirst({ const team = await prisma.team.findFirst({
where: buildTeamWhereQuery({ where: buildTeamWhereQuery({
teamId, teamId,

View File

@ -11,6 +11,12 @@ export const deleteTeamRoute = authenticatedProcedure
const { teamId } = input; const { teamId } = input;
const { user } = ctx; const { user } = ctx;
ctx.logger.info({
input: {
teamId,
},
});
await deleteTeam({ await deleteTeam({
userId: user.id, userId: user.id,
teamId, teamId,

View File

@ -20,6 +20,13 @@ export const findTeamGroupsRoute = authenticatedProcedure
const { teamId, types, query, page, perPage, teamGroupId, organisationRoles } = input; const { teamId, types, query, page, perPage, teamGroupId, organisationRoles } = input;
const { user } = ctx; const { user } = ctx;
ctx.logger.info({
input: {
teamId,
teamGroupId,
},
});
return await findTeamGroups({ return await findTeamGroups({
userId: user.id, userId: user.id,
teamId, teamId,

View File

@ -13,6 +13,12 @@ export const findTeamMembersRoute = authenticatedProcedure
const { teamId, query, page, perPage } = input; const { teamId, query, page, perPage } = input;
const { user } = ctx; const { user } = ctx;
ctx.logger.info({
input: {
teamId,
},
});
return await findTeamMembers({ return await findTeamMembers({
userId: user.id, userId: user.id,
teamId, teamId,

View File

@ -11,5 +11,11 @@ export const findTeamsRoute = authenticatedProcedure
const { organisationId } = input; const { organisationId } = input;
const { user } = ctx; const { user } = ctx;
ctx.logger.info({
input: {
organisationId,
},
});
return findTeams({ userId: user.id, organisationId }); return findTeams({ userId: user.id, organisationId });
}); });

View File

@ -14,6 +14,12 @@ export const getTeamMembersRoute = authenticatedProcedure
const { teamId } = input; const { teamId } = input;
const { user } = ctx; const { user } = ctx;
ctx.logger.info({
input: {
teamId,
},
});
return await getTeamMembers({ return await getTeamMembers({
userId: user.id, userId: user.id,
teamId, teamId,

View File

@ -8,8 +8,16 @@ export const getTeamRoute = authenticatedProcedure
.input(ZGetTeamRequestSchema) .input(ZGetTeamRequestSchema)
.output(ZGetTeamResponseSchema) .output(ZGetTeamResponseSchema)
.query(async ({ input, ctx }) => { .query(async ({ input, ctx }) => {
const { teamReference } = input;
ctx.logger.info({
input: {
teamReference,
},
});
return await getTeam({ return await getTeam({
teamReference: input.teamReference, teamReference,
userId: ctx.user.id, userId: ctx.user.id,
}); });
}); });

View File

@ -61,6 +61,12 @@ export const teamRouter = router({
update: authenticatedProcedure update: authenticatedProcedure
.input(ZUpdateTeamEmailMutationSchema) .input(ZUpdateTeamEmailMutationSchema)
.mutation(async ({ input, ctx }) => { .mutation(async ({ input, ctx }) => {
ctx.logger.info({
input: {
teamId: input.teamId,
},
});
return await updateTeamEmail({ return await updateTeamEmail({
userId: ctx.user.id, userId: ctx.user.id,
...input, ...input,
@ -69,39 +75,71 @@ export const teamRouter = router({
delete: authenticatedProcedure delete: authenticatedProcedure
.input(ZDeleteTeamEmailMutationSchema) .input(ZDeleteTeamEmailMutationSchema)
.mutation(async ({ input, ctx }) => { .mutation(async ({ input, ctx }) => {
const { teamId } = input;
ctx.logger.info({
input: {
teamId,
},
});
return await deleteTeamEmail({ return await deleteTeamEmail({
userId: ctx.user.id, userId: ctx.user.id,
userEmail: ctx.user.email, userEmail: ctx.user.email,
...input, teamId,
}); });
}), }),
verification: { verification: {
send: authenticatedProcedure send: authenticatedProcedure
.input(ZCreateTeamEmailVerificationMutationSchema) .input(ZCreateTeamEmailVerificationMutationSchema)
.mutation(async ({ input, ctx }) => { .mutation(async ({ input, ctx }) => {
const { teamId, email, name } = input;
ctx.logger.info({
input: {
teamId,
},
});
return await createTeamEmailVerification({ return await createTeamEmailVerification({
teamId: input.teamId, teamId,
userId: ctx.user.id, userId: ctx.user.id,
data: { data: {
email: input.email, email,
name: input.name, name,
}, },
}); });
}), }),
resend: authenticatedProcedure resend: authenticatedProcedure
.input(ZResendTeamEmailVerificationMutationSchema) .input(ZResendTeamEmailVerificationMutationSchema)
.mutation(async ({ input, ctx }) => { .mutation(async ({ input, ctx }) => {
const { teamId } = input;
ctx.logger.info({
input: {
teamId,
},
});
await resendTeamEmailVerification({ await resendTeamEmailVerification({
userId: ctx.user.id, userId: ctx.user.id,
...input, teamId,
}); });
}), }),
delete: authenticatedProcedure delete: authenticatedProcedure
.input(ZDeleteTeamEmailVerificationMutationSchema) .input(ZDeleteTeamEmailVerificationMutationSchema)
.mutation(async ({ input, ctx }) => { .mutation(async ({ input, ctx }) => {
const { teamId } = input;
ctx.logger.info({
input: {
teamId,
},
});
return await deleteTeamEmailVerification({ return await deleteTeamEmailVerification({
userId: ctx.user.id, userId: ctx.user.id,
...input, teamId,
}); });
}), }),
}, },

View File

@ -19,6 +19,15 @@ export const updateTeamGroupRoute = authenticatedProcedure
const { id, data } = input; const { id, data } = input;
const { user } = ctx; const { user } = ctx;
ctx.logger.info({
input: {
id,
data: {
teamRole: data.teamRole,
},
},
});
const teamGroup = await prisma.teamGroup.findFirst({ const teamGroup = await prisma.teamGroup.findFirst({
where: { where: {
id, id,

View File

@ -22,6 +22,13 @@ export const updateTeamMemberRoute = authenticatedProcedure
const { teamId, memberId, data } = input; const { teamId, memberId, data } = input;
const userId = ctx.user.id; const userId = ctx.user.id;
ctx.logger.info({
input: {
teamId,
memberId,
},
});
const team = await prisma.team.findFirst({ const team = await prisma.team.findFirst({
where: { where: {
AND: [ AND: [

View File

@ -16,6 +16,12 @@ export const updateTeamSettingsRoute = authenticatedProcedure
const { user } = ctx; const { user } = ctx;
const { teamId, data } = input; const { teamId, data } = input;
ctx.logger.info({
input: {
teamId,
},
});
const { const {
// Document related settings. // Document related settings.
documentVisibility, documentVisibility,

View File

@ -13,6 +13,12 @@ export const updateTeamRoute = authenticatedProcedure
const { name, url, profileBio, profileEnabled } = data; const { name, url, profileBio, profileEnabled } = data;
ctx.logger.info({
input: {
teamId,
},
});
if (name || url) { if (name || url) {
await updateTeam({ await updateTeam({
userId: ctx.user.id, userId: ctx.user.id,

View File

@ -67,6 +67,12 @@ export const templateRouter = router({
.query(async ({ input, ctx }) => { .query(async ({ input, ctx }) => {
const { teamId } = ctx; const { teamId } = ctx;
ctx.logger.info({
input: {
folderId: input.folderId,
},
});
return await findTemplates({ return await findTemplates({
userId: ctx.user.id, userId: ctx.user.id,
teamId, teamId,
@ -92,6 +98,12 @@ export const templateRouter = router({
const { teamId } = ctx; const { teamId } = ctx;
const { templateId } = input; const { templateId } = input;
ctx.logger.info({
input: {
templateId,
},
});
return await getTemplateById({ return await getTemplateById({
id: templateId, id: templateId,
userId: ctx.user.id, userId: ctx.user.id,
@ -120,6 +132,12 @@ export const templateRouter = router({
const { teamId } = ctx; const { teamId } = ctx;
const { title, templateDocumentDataId, folderId } = input; const { title, templateDocumentDataId, folderId } = input;
ctx.logger.info({
input: {
folderId,
},
});
return await createTemplate({ return await createTemplate({
userId: ctx.user.id, userId: ctx.user.id,
teamId, teamId,
@ -146,9 +164,14 @@ export const templateRouter = router({
.mutation(async ({ input, ctx }) => { .mutation(async ({ input, ctx }) => {
const { teamId } = ctx; const { teamId } = ctx;
const { templateId, data, meta } = input; const { templateId, data, meta } = input;
const userId = ctx.user.id; const userId = ctx.user.id;
ctx.logger.info({
input: {
templateId,
},
});
return await updateTemplate({ return await updateTemplate({
userId, userId,
teamId, teamId,
@ -176,6 +199,12 @@ export const templateRouter = router({
const { teamId } = ctx; const { teamId } = ctx;
const { templateId } = input; const { templateId } = input;
ctx.logger.info({
input: {
templateId,
},
});
return await duplicateTemplate({ return await duplicateTemplate({
userId: ctx.user.id, userId: ctx.user.id,
teamId, teamId,
@ -200,9 +229,14 @@ export const templateRouter = router({
.mutation(async ({ input, ctx }) => { .mutation(async ({ input, ctx }) => {
const { teamId } = ctx; const { teamId } = ctx;
const { templateId } = input; const { templateId } = input;
const userId = ctx.user.id; const userId = ctx.user.id;
ctx.logger.info({
input: {
templateId,
},
});
await deleteTemplate({ userId, id: templateId, teamId }); await deleteTemplate({ userId, id: templateId, teamId });
return ZGenericSuccessResponse; return ZGenericSuccessResponse;
@ -228,6 +262,12 @@ export const templateRouter = router({
const { templateId, recipients, distributeDocument, customDocumentDataId, prefillFields } = const { templateId, recipients, distributeDocument, customDocumentDataId, prefillFields } =
input; input;
ctx.logger.info({
input: {
templateId,
},
});
const limits = await getServerLimits({ userId: ctx.user.id, teamId }); const limits = await getServerLimits({ userId: ctx.user.id, teamId });
if (limits.remaining.documents === 0) { if (limits.remaining.documents === 0) {
@ -291,6 +331,12 @@ export const templateRouter = router({
templateUpdatedAt, templateUpdatedAt,
} = input; } = input;
ctx.logger.info({
input: {
directTemplateToken,
},
});
return await createDocumentFromDirectTemplate({ return await createDocumentFromDirectTemplate({
directRecipientName, directRecipientName,
directRecipientEmail, directRecipientEmail,
@ -330,6 +376,13 @@ export const templateRouter = router({
const userId = ctx.user.id; const userId = ctx.user.id;
ctx.logger.info({
input: {
templateId,
directRecipientId,
},
});
const template = await getTemplateById({ id: templateId, teamId, userId: ctx.user.id }); const template = await getTemplateById({ id: templateId, teamId, userId: ctx.user.id });
const limits = await getServerLimits({ userId: ctx.user.id, teamId: template.teamId }); const limits = await getServerLimits({ userId: ctx.user.id, teamId: template.teamId });
@ -364,6 +417,12 @@ export const templateRouter = router({
const userId = ctx.user.id; const userId = ctx.user.id;
ctx.logger.info({
input: {
templateId,
},
});
await deleteTemplateDirectLink({ userId, teamId, templateId }); await deleteTemplateDirectLink({ userId, teamId, templateId });
return ZGenericSuccessResponse; return ZGenericSuccessResponse;
@ -390,6 +449,12 @@ export const templateRouter = router({
const userId = ctx.user.id; const userId = ctx.user.id;
ctx.logger.info({
input: {
templateId,
},
});
return await toggleTemplateDirectLink({ userId, teamId, templateId, enabled }); return await toggleTemplateDirectLink({ userId, teamId, templateId, enabled });
}), }),
@ -402,6 +467,13 @@ export const templateRouter = router({
const { templateId, teamId, csv, sendImmediately } = input; const { templateId, teamId, csv, sendImmediately } = input;
const { user } = ctx; const { user } = ctx;
ctx.logger.info({
input: {
templateId,
teamId,
},
});
if (csv.length > 4 * 1024 * 1024) { if (csv.length > 4 * 1024 * 1024) {
throw new TRPCError({ throw new TRPCError({
code: 'BAD_REQUEST', code: 'BAD_REQUEST',

View File

@ -4,7 +4,9 @@ import type { AnyZodObject } from 'zod';
import { AppError, genericErrorCodeToTrpcErrorCodeMap } from '@documenso/lib/errors/app-error'; import { AppError, genericErrorCodeToTrpcErrorCodeMap } from '@documenso/lib/errors/app-error';
import { getApiTokenByToken } from '@documenso/lib/server-only/public-api/get-api-token-by-token'; 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 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 { isAdmin } from '@documenso/lib/utils/is-admin';
import type { TrpcContext } from './context'; import type { TrpcContext } from './context';
@ -66,11 +68,13 @@ const t = initTRPC
* Middlewares * Middlewares
*/ */
export const authenticatedMiddleware = t.middleware(async ({ ctx, next, path }) => { export const authenticatedMiddleware = t.middleware(async ({ ctx, next, path }) => {
const logger = ctx.logger.child({ const infoToLog: TrpcApiLog = {
path, path,
auth: ctx.metadata.auth, auth: ctx.metadata.auth,
source: ctx.metadata.source, source: ctx.metadata.source,
}); trpcMiddleware: 'authenticated',
unverifiedTeamId: ctx.teamId,
};
const authorizationHeader = ctx.req.headers.get('authorization'); 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 }); const apiToken = await getApiTokenByToken({ token });
logger.info({ ctx.logger.info({
...infoToLog,
userId: apiToken.user.id, userId: apiToken.user.id,
apiTokenId: apiToken.id, apiTokenId: apiToken.id,
}); } satisfies TrpcApiLog);
return await next({ return await next({
ctx: { 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, userId: ctx.user.id,
apiTokenId: null, apiTokenId: null,
}); } satisfies TrpcApiLog);
return await next({ return await next({
ctx: { ctx: {
...ctx, ...ctx,
logger: trpcSessionLogger,
user: ctx.user, user: ctx.user,
session: ctx.session, session: ctx.session,
metadata: { 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({ return await next({
ctx: { ctx: {
...ctx, ...ctx,
logger: trpcSessionLogger,
user: ctx.user, user: ctx.user,
session: ctx.session, session: ctx.session,
metadata: { 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) { if (!ctx.session || !ctx.user) {
throw new TRPCError({ throw new TRPCError({
code: 'UNAUTHORIZED', 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({ return await next({
ctx: { ctx: {
...ctx, ...ctx,
logger: trpcSessionLogger,
user: ctx.user, user: ctx.user,
session: ctx.session, session: ctx.session,
metadata: { 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 * Routers and Procedures
*/ */
export const router = t.router; export const router = t.router;
export const procedure = t.procedure; export const procedure = t.procedure.use(procedureMiddleware);
export const authenticatedProcedure = t.procedure.use(authenticatedMiddleware); export const authenticatedProcedure = t.procedure.use(authenticatedMiddleware);
// While this is functionally the same as `procedure`, it's useful for indicating purpose // While this is functionally the same as `procedure`, it's useful for indicating purpose
export const maybeAuthenticatedProcedure = t.procedure.use(maybeAuthenticatedMiddleware); export const maybeAuthenticatedProcedure = t.procedure.use(maybeAuthenticatedMiddleware);

View File

@ -19,6 +19,12 @@ export const webhookRouter = router({
.query(async ({ ctx, input }) => { .query(async ({ ctx, input }) => {
const { teamId } = input; const { teamId } = input;
ctx.logger.info({
input: {
teamId,
},
});
return await getWebhooksByTeamId(teamId, ctx.user.id); return await getWebhooksByTeamId(teamId, ctx.user.id);
}), }),
@ -27,6 +33,13 @@ export const webhookRouter = router({
.query(async ({ input, ctx }) => { .query(async ({ input, ctx }) => {
const { id, teamId } = input; const { id, teamId } = input;
ctx.logger.info({
input: {
id,
teamId,
},
});
return await getWebhookById({ return await getWebhookById({
id, id,
userId: ctx.user.id, userId: ctx.user.id,
@ -39,6 +52,12 @@ export const webhookRouter = router({
.mutation(async ({ input, ctx }) => { .mutation(async ({ input, ctx }) => {
const { enabled, eventTriggers, secret, webhookUrl, teamId } = input; const { enabled, eventTriggers, secret, webhookUrl, teamId } = input;
ctx.logger.info({
input: {
teamId,
},
});
return await createWebhook({ return await createWebhook({
enabled, enabled,
secret, secret,
@ -54,6 +73,13 @@ export const webhookRouter = router({
.mutation(async ({ input, ctx }) => { .mutation(async ({ input, ctx }) => {
const { id, teamId } = input; const { id, teamId } = input;
ctx.logger.info({
input: {
id,
teamId,
},
});
return await deleteWebhookById({ return await deleteWebhookById({
id, id,
teamId, teamId,
@ -66,6 +92,13 @@ export const webhookRouter = router({
.mutation(async ({ input, ctx }) => { .mutation(async ({ input, ctx }) => {
const { id, teamId, ...data } = input; const { id, teamId, ...data } = input;
ctx.logger.info({
input: {
id,
teamId,
},
});
return await editWebhook({ return await editWebhook({
id, id,
data, data,

View File

@ -1,14 +1,14 @@
import type { ErrorHandlerOptions } from '@trpc/server/unstable-core-do-not-import'; import type { ErrorHandlerOptions } from '@trpc/server/unstable-core-do-not-import';
import { AppError, AppErrorCode } from '@documenso/lib/errors/app-error'; 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<NonNullable<Parameters<typeof trpcServer>[0]['onError']>>[0], // :-) // Parameters<NonNullable<Parameters<typeof trpcServer>[0]['onError']>>[0], // :-)
export const handleTrpcRouterError = ( export const handleTrpcRouterError = (
{ error, path }: Pick<ErrorHandlerOptions<undefined>, 'error' | 'path'>, { error, ctx }: Pick<ErrorHandlerOptions<TrpcContext>, 'error' | 'path' | 'ctx'>,
source: 'trpc' | 'apiV1' | 'apiV2', _source: 'trpc' | 'apiV1' | 'apiV2',
) => { ) => {
const appError = AppError.parseError(error.cause || error); const appError = AppError.parseError(error.cause || error);
@ -23,16 +23,16 @@ export const handleTrpcRouterError = (
// not an AppError. // not an AppError.
const isLoggableTrpcError = !isAppError && errorCodesToAlertOn.includes(error.code); const isLoggableTrpcError = !isAppError && errorCodesToAlertOn.includes(error.code);
if (isLoggableAppError || isLoggableTrpcError) { const errorLogger = (ctx?.logger || logger).child({
console.error(error); status: 'error',
appError: AppError.toJSON(appError),
});
logger.error(error, { // Only fully log the error on certain conditions since some errors are expected.
method: path, if (isLoggableAppError || isLoggableTrpcError) {
context: { errorLogger.error(error);
source, } else {
appError: AppError.toJSON(appError), errorLogger.info('TRPC_ERROR_HANDLER');
},
});
} }
}; };

View File

@ -15,6 +15,8 @@ declare namespace NodeJS {
NEXT_PRIVATE_ENCRYPTION_KEY: string; NEXT_PRIVATE_ENCRYPTION_KEY: string;
NEXT_PRIVATE_ENCRYPTION_SECONDARY_KEY: string; NEXT_PRIVATE_ENCRYPTION_SECONDARY_KEY: string;
NEXT_PRIVATE_LOGGER_FILE_PATH?: string;
NEXT_PRIVATE_STRIPE_API_KEY: string; NEXT_PRIVATE_STRIPE_API_KEY: string;
NEXT_PRIVATE_STRIPE_WEBHOOK_SECRET: string; NEXT_PRIVATE_STRIPE_WEBHOOK_SECRET: string;
@ -77,8 +79,6 @@ declare namespace NodeJS {
NEXT_PRIVATE_INNGEST_APP_ID?: string; NEXT_PRIVATE_INNGEST_APP_ID?: string;
NEXT_PRIVATE_INNGEST_EVENT_KEY?: string; NEXT_PRIVATE_INNGEST_EVENT_KEY?: string;
NEXT_PRIVATE_LOGGER_HONEY_BADGER_API_KEY?: string;
POSTGRES_URL?: string; POSTGRES_URL?: string;
DATABASE_URL?: string; DATABASE_URL?: string;
POSTGRES_PRISMA_URL?: string; POSTGRES_PRISMA_URL?: string;

View File

@ -50,6 +50,7 @@
"NEXT_PUBLIC_DOCUMENT_SIZE_UPLOAD_LIMIT", "NEXT_PUBLIC_DOCUMENT_SIZE_UPLOAD_LIMIT",
"NEXT_PRIVATE_DATABASE_URL", "NEXT_PRIVATE_DATABASE_URL",
"NEXT_PRIVATE_DIRECT_DATABASE_URL", "NEXT_PRIVATE_DIRECT_DATABASE_URL",
"NEXT_PRIVATE_LOGGER_FILE_PATH",
"NEXT_PRIVATE_SIGNING_TRANSPORT", "NEXT_PRIVATE_SIGNING_TRANSPORT",
"NEXT_PRIVATE_SIGNING_PASSPHRASE", "NEXT_PRIVATE_SIGNING_PASSPHRASE",
"NEXT_PRIVATE_SIGNING_LOCAL_FILE_PATH", "NEXT_PRIVATE_SIGNING_LOCAL_FILE_PATH",
@ -104,7 +105,6 @@
"NEXT_PRIVATE_BROWSERLESS_URL", "NEXT_PRIVATE_BROWSERLESS_URL",
"NEXT_PRIVATE_JOBS_PROVIDER", "NEXT_PRIVATE_JOBS_PROVIDER",
"NEXT_PRIVATE_INNGEST_APP_ID", "NEXT_PRIVATE_INNGEST_APP_ID",
"NEXT_PRIVATE_LOGGER_HONEY_BADGER_API_KEY",
"INNGEST_EVENT_KEY", "INNGEST_EVENT_KEY",
"NEXT_PRIVATE_INNGEST_EVENT_KEY", "NEXT_PRIVATE_INNGEST_EVENT_KEY",
"CI", "CI",