From 52a840f4a8385584bfc80acc22bfc7c2c7128d03 Mon Sep 17 00:00:00 2001 From: Leonardo Kewitz Date: Tue, 11 Feb 2020 11:35:37 -0300 Subject: [PATCH] fix: debug memory leak See: https://github.com/visionmedia/debug/issues/678 --- server/graphql/v1/utils.js | 10 +++++---- server/lib/backyourstack/dispatcher.js | 3 ++- server/lib/email.js | 9 +++++--- server/lib/express.js | 5 +++-- server/lib/notifications.js | 3 ++- server/middleware/security/auth.js | 22 +++++++++++--------- server/middleware/security/authentication.js | 9 ++++---- server/models/PaymentMethod.js | 4 ++-- server/models/index.js | 7 ++++--- server/routes.js | 20 +++++++++++------- 10 files changed, 54 insertions(+), 38 deletions(-) diff --git a/server/graphql/v1/utils.js b/server/graphql/v1/utils.js index 4291f1ba5f13..2403bd03233c 100644 --- a/server/graphql/v1/utils.js +++ b/server/graphql/v1/utils.js @@ -1,8 +1,10 @@ -import debug from 'debug'; +import debugLib from 'debug'; import { graphql } from 'graphql'; import { loaders } from '../loaders'; import schema from './schema'; +const debug = debugLib('graphql'); + export const makeRequest = (remoteUser, query) => { return { remoteUser, @@ -22,9 +24,9 @@ export const graphqlQuery = async (query, variables, remoteUser) => { }; if (process.env.DEBUG && process.env.DEBUG.match(/graphql/)) { - debug('graphql')('query', query); - debug('graphql')('variables', variables); - debug('graphql')('context', remoteUser); + debug('query', query); + debug('variables', variables); + debug('context', remoteUser); } return prepare().then(() => diff --git a/server/lib/backyourstack/dispatcher.js b/server/lib/backyourstack/dispatcher.js index 913e434f5cba..7dac035a7e44 100644 --- a/server/lib/backyourstack/dispatcher.js +++ b/server/lib/backyourstack/dispatcher.js @@ -10,6 +10,8 @@ import status from '../../constants/order_status'; import activities from '../../constants/activities'; import * as paymentsLib from '../payments'; +const debug = debugLib('dispatch_prepaid_subscription'); + export function needsDispatching(nextDispatchDate) { const needs = moment(nextDispatchDate).isSameOrBefore(); return needs; @@ -61,7 +63,6 @@ async function createPaymentMethod(originalCreditTransaction) { } export async function dispatchFunds(order) { - const debug = debugLib('dispatch_prepaid_subscription'); // Amount shareable amongst dependencies const transaction = await models.Transaction.findOne({ where: { OrderId: order.id, type: 'CREDIT' }, diff --git a/server/lib/email.js b/server/lib/email.js index ffe3842a0586..21480b156d0f 100644 --- a/server/lib/email.js +++ b/server/lib/email.js @@ -15,6 +15,9 @@ import whiteListDomains from './whiteListDomains'; import { md5 } from './utils'; const debug = debugLib('email'); +const debugData = debugLib('data'); +const debugText = debugLib('text'); +const debugHtml = debugLib('html'); export const getMailer = () => { if (config.maildev.client) { @@ -48,7 +51,7 @@ const render = (template, data) => { // When in development mode, we log the data used to compile the template // (useful to get login token without sending an email) - debugLib('data')(`Rendering ${template} with data`, data); + debugData(`Rendering ${template} with data`, data); return { text, html }; }; @@ -185,8 +188,8 @@ const sendMessage = (recipients, subject, html, options = {}) => { }); } else { debug('>>> mailer not configured'); - debugLib('text')(options.text); - debugLib('html')(html); + debugText(options.text); + debugHtml(html); return Promise.resolve(); } }; diff --git a/server/lib/express.js b/server/lib/express.js index 82428452fd9e..b559ca4577cd 100644 --- a/server/lib/express.js +++ b/server/lib/express.js @@ -33,14 +33,15 @@ export default function(app) { app.use(loadersMiddleware); if (process.env.DEBUG && process.env.DEBUG.match(/response/)) { + const debugResponse = debug('response'); app.use((req, res, next) => { const temp = res.end; res.end = function(str) { try { const obj = JSON.parse(str); - debug('response')(JSON.stringify(obj, null, ' ')); + debugResponse(JSON.stringify(obj, null, ' ')); } catch (e) { - debug('response', str); + debugResponse(str); } temp.apply(this, arguments); }; diff --git a/server/lib/notifications.js b/server/lib/notifications.js index 949a439052a6..a1ad1e0108bc 100644 --- a/server/lib/notifications.js +++ b/server/lib/notifications.js @@ -15,6 +15,7 @@ import { sanitizeActivity, enrichActivity } from './webhooks'; import { PayoutMethodTypes } from '../models/PayoutMethod'; const debug = debugLib('notification'); +const debugActivityData = debugLib('activity.data'); export default async (Sequelize, activity) => { // publish everything to our private channel @@ -186,7 +187,7 @@ async function notifyMembersOfCollective(CollectiveId, activity, options) { async function notifyByEmail(activity) { debug('notifyByEmail', activity.type); - debugLib('activity.data')('activity.data', activity.data); + debugActivityData('activity.data', activity.data); switch (activity.type) { case activityType.TICKET_CONFIRMED: notifyUserId(activity.data.UserId, activity); diff --git a/server/middleware/security/auth.js b/server/middleware/security/auth.js index bb4ecd8f1933..e64c11f017ff 100644 --- a/server/middleware/security/auth.js +++ b/server/middleware/security/auth.js @@ -1,5 +1,5 @@ import config from 'config'; -import debug from 'debug'; +import debugLib from 'debug'; import errors from '../../lib/errors'; import { authenticateUser } from './authentication'; @@ -8,6 +8,8 @@ import models from '../../models'; const { Unauthorized } = errors; +const debug = debugLib('auth'); + /** * Check Client App * @@ -21,7 +23,7 @@ export async function checkClientApp(req, res, next) { where: { type: 'apiKey', apiKey }, }); if (app) { - debug('auth')('Valid Client App (apiKey)'); + debug('Valid Client App (apiKey)'); req.clientApp = app; const collectiveId = app.CollectiveId; if (collectiveId) { @@ -35,7 +37,7 @@ export async function checkClientApp(req, res, next) { } next(); } else { - debug('auth')(`Invalid Client App (apiKey: ${apiKey}).`); + debug(`Invalid Client App (apiKey: ${apiKey}).`); next(new Unauthorized(`Invalid Api Key: ${apiKey}.`)); } } else if (clientId) { @@ -44,16 +46,16 @@ export async function checkClientApp(req, res, next) { where: { clientId }, }); if (app) { - debug('auth')('Valid Client App'); + debug('Valid Client App'); req.clientApp = app; next(); } else { - debug('auth')(`Invalid Client App (clientId: ${clientId}).`); + debug(`Invalid Client App (clientId: ${clientId}).`); next(new Unauthorized(`Invalid Client Id: ${clientId}.`)); } } else { next(); - debug('auth')('No Client App'); + debug('No Client App'); } } @@ -94,16 +96,16 @@ export function authorizeClientApp(req, res, next) { const apiKey = req.get('Api-Key') || req.query.apiKey || req.query.api_key || req.body.api_key; if (req.clientApp) { - debug('auth')('Valid Client App'); + debug('Valid Client App'); next(); } else if (apiKey === config.keys.opencollective.apiKey) { - debug('auth')(`Valid API key: ${apiKey}`); + debug(`Valid API key: ${apiKey}`); next(); } else if (apiKey) { - debug('auth')(`Invalid API key: ${apiKey}`); + debug(`Invalid API key: ${apiKey}`); next(new Unauthorized(`Invalid API key: ${apiKey}`)); } else { - debug('auth')('Missing API key or Client Id'); + debug('Missing API key or Client Id'); next(); } } diff --git a/server/middleware/security/authentication.js b/server/middleware/security/authentication.js index 2ca58941b505..96fd9a5e0ae0 100644 --- a/server/middleware/security/authentication.js +++ b/server/middleware/security/authentication.js @@ -1,4 +1,4 @@ -import debug from 'debug'; +import debugLib from 'debug'; import config from 'config'; import jwt from 'jsonwebtoken'; import passport from 'passport'; @@ -20,6 +20,7 @@ const { User } = models; const { BadRequest, CustomError } = errors; const { jwtSecret } = config.keys.opencollective; +const debug = debugLib('auth'); /** * Middleware related to authentication. @@ -128,7 +129,7 @@ export const _authenticateUserByJwt = async (req, res, next) => { req.remoteUser = user; - debug('auth')('logged in user', req.remoteUser.id, 'roles:', req.remoteUser.rolesByCollectiveId); + debug('logged in user', req.remoteUser.id, 'roles:', req.remoteUser.rolesByCollectiveId); next(); }; @@ -147,14 +148,14 @@ export function authenticateUser(req, res, next) { parseJwtNoExpiryCheck(req, res, e => { // If a token was submitted but is invalid, we continue without authenticating the user if (e) { - debug('auth')('>>> checkJwtExpiry invalid error', e); + debug('>>> checkJwtExpiry invalid error', e); return next(); } checkJwtExpiry(req, res, e => { // If a token was submitted and is expired, we continue without authenticating the user if (e) { - debug('auth')('>>> checkJwtExpiry expiry error', e); + debug('>>> checkJwtExpiry expiry error', e); return next(); } _authenticateUserByJwt(req, res, next); diff --git a/server/models/PaymentMethod.js b/server/models/PaymentMethod.js index aec8991860f2..24e1d6fe6fdf 100644 --- a/server/models/PaymentMethod.js +++ b/server/models/PaymentMethod.js @@ -1,6 +1,6 @@ /** @module models/PaymentMethod */ -import libdebug from 'debug'; +import debugLib from 'debug'; import Promise from 'bluebird'; import { get, intersection } from 'lodash'; import { Op } from 'sequelize'; @@ -17,7 +17,7 @@ import { isTestToken } from '../lib/stripe'; import { maxInteger } from '../constants/math'; -const debug = libdebug('PaymentMethod'); +const debug = debugLib('PaymentMethod'); export default function(Sequelize, DataTypes) { const { models } = Sequelize; diff --git a/server/models/index.js b/server/models/index.js index 002e3c5a6b94..59fa4d06621d 100644 --- a/server/models/index.js +++ b/server/models/index.js @@ -1,7 +1,7 @@ import pg from 'pg'; import Sequelize from 'sequelize'; import config from 'config'; -import debug from 'debug'; +import debugLib from 'debug'; import logger from '../lib/logger'; import { getDBConf } from '../lib/db'; @@ -11,6 +11,7 @@ import { getDBConf } from '../lib/db'; pg.defaults.parseInt8 = true; const dbConfig = getDBConf('database'); +const debug = debug('psql'); /** * Database connection. @@ -26,12 +27,12 @@ if (config.database.options.logging) { if (process.env.NODE_ENV === 'production') { config.database.options.logging = (query, executionTime) => { if (executionTime > 50) { - debug('psql')(query.replace(/(\n|\t| +)/g, ' ').slice(0, 100), '|', executionTime, 'ms'); + debug(query.replace(/(\n|\t| +)/g, ' ').slice(0, 100), '|', executionTime, 'ms'); } }; } else { config.database.options.logging = (query, executionTime) => { - debug('psql')( + debug( '\n-------------------- --------------------\n', query, `\n-------------------- --------------------\n`, diff --git a/server/routes.js b/server/routes.js index 16420a8e6fb7..c0ffd7b3a79b 100644 --- a/server/routes.js +++ b/server/routes.js @@ -94,21 +94,25 @@ export default app => { }); app.use('/graphql', rateLimiter); } - if (process.env.DEBUG) { + const debugOperation = debug('operation'); + const debugParams = debug('params'); + const debugHeaders = debug('headers'); + const debugCurl = debug('curl'); + app.use('*', (req, res, next) => { const body = sanitizeForLogs(req.body || {}); - debug('operation')(body.operationName, JSON.stringify(body.variables, null)); + debugOperation(body.operationName, JSON.stringify(body.variables, null)); if (body.query) { const query = body.query; - debug('params')(query); + debugParams(query); delete body.query; } - debug('params')('req.query', req.query); - debug('params')('req.body', JSON.stringify(body, null, ' ')); - debug('params')('req.params', req.params); - debug('headers')('req.headers', req.headers); - debug('curl')('curl', curlify(req, req.body)); + debugParams('req.query', req.query); + debugParams('req.body', JSON.stringify(body, null, ' ')); + debugParams('req.params', req.params); + debugHeaders('req.headers', req.headers); + debugCurl('curl', curlify(req, req.body)); next(); }); }