From 2c21a1246ab064bef6470961f39bbad76b0eaef0 Mon Sep 17 00:00:00 2001 From: Justin Wind Date: Thu, 22 Feb 2024 16:24:59 -0800 Subject: [PATCH] support async log storate, fix lint issues --- server.js | 14 +++++++++----- src/authenticator.js | 6 +++--- src/db/errors.js | 34 +++++++++++++++++----------------- src/service.js | 11 +++++++++-- src/template/info-html.js | 2 +- src/template/report-html.js | 10 ++++++++-- test/src/service.js | 21 +++++++++++++++------ 7 files changed, 62 insertions(+), 36 deletions(-) diff --git a/server.js b/server.js index 17302b7..979c1e0 100644 --- a/server.js +++ b/server.js @@ -1,7 +1,8 @@ /* eslint-disable capitalized-comments */ 'use strict'; -const http = require('http'); +const http = require('node:http'); +const { AsyncLocalStorage } = require('node:async_hooks'); const DB = require('./src/db'); const Logger = require('./src/logger'); @@ -14,11 +15,12 @@ const _scope = fileScope(__filename)('main'); const PORT = process.env.PORT || 3001; const ADDR = process.env.LISTEN_ADDR || '127.0.0.1'; +const asyncLocalStorage = new AsyncLocalStorage(); const logger = new Logger({ // ignoreBelowLevel: 'info', -}); +}, {}, asyncLocalStorage); const db = new DB(logger, { - connectionString: 'postgresql://%2Fhome%2Ffek%2Fprojects%2Fhubl%2Fnodejs%2Fpostgres_dev-13/urlittler', + connectionString: 'postgresql://%2Fhome%2Ffek%2Fprojects%2F_db%2Fpostgres_dev-15/urlittler', // connectionString: 'sqlite://', }); const service = new Service(logger, db, { @@ -26,10 +28,12 @@ const service = new Service(logger, db, { manager: { selfBaseUrl: process.env.BASE_URL || '', }, -}); +}, asyncLocalStorage); http.createServer((req, res) => { - service.dispatch(req, res); + asyncLocalStorage.run({}, async () => { + await service.dispatch(req, res); + }); }).listen(PORT, ADDR, (err) => { if (err) { logger.error(_scope, 'error starting server:', err); diff --git a/src/authenticator.js b/src/authenticator.js index f3d33ec..c960078 100644 --- a/src/authenticator.js +++ b/src/authenticator.js @@ -114,7 +114,7 @@ class Authenticator { const secret = authData && authData.secret; if (!secret) { - this.logger.debug(_scope, 'failed, invalid authenticationId', { ctx }) + this.logger.debug(_scope, 'failed, invalid authenticationId', { ctx }); return false; } @@ -123,7 +123,7 @@ class Authenticator { const currentEpoch = Date.now() / 1000; ctx.authenticationDrift = currentEpoch - ctx.authenticationEpoch; if (Math.abs(ctx.authenticationDrift) > this.timestampGraceMs) { - this.logger.debug(_scope, 'failed, invalid timestamp', { ctx }) + this.logger.debug(_scope, 'failed, invalid timestamp', { ctx }); return false; } @@ -165,7 +165,7 @@ class Authenticator { // Update pwhash // authData.password = await argon2.hash(newPassword, { type: argon2.id }); if (authData.password.startsWith('$argon2')) { - if (await argon2.verify(authdata.password, authenticationPass)) { + if (await argon2.verify(authData.password, authenticationPass)) { this.logger.debug(_scope, 'failed argon2 verify', { ctx }); return false; } else { diff --git a/src/db/errors.js b/src/db/errors.js index f13baba..4e5aa4a 100644 --- a/src/db/errors.js +++ b/src/db/errors.js @@ -1,30 +1,30 @@ 'use strict'; - const { DatabaseError } = require('../errors'); +const { DatabaseError } = require('../errors'); - class NotImplemented extends DatabaseError { - constructor(...args) { - super(...args); - Error.captureStackTrace(NotImplemented); - } +class NotImplemented extends DatabaseError { + constructor(...args) { + super(...args); + Error.captureStackTrace(NotImplemented); } +} - class UnexpectedResult extends DatabaseError { - constructor(...args) { - super(...args); - Error.captureStackTrace(UnexpectedResult); - } +class UnexpectedResult extends DatabaseError { + constructor(...args) { + super(...args); + Error.captureStackTrace(UnexpectedResult); } +} - class UnsupportedEngine extends DatabaseError { - constructor(...args) { - super(...args); - Error.captureStackTrace(UnsupportedEngine); - } +class UnsupportedEngine extends DatabaseError { + constructor(...args) { + super(...args); + Error.captureStackTrace(UnsupportedEngine); } +} module.exports = { NotImplemented, UnexpectedResult, UnsupportedEngine, -}; \ No newline at end of file +}; diff --git a/src/service.js b/src/service.js index 97ed85e..1596c00 100644 --- a/src/service.js +++ b/src/service.js @@ -23,10 +23,11 @@ const defaultOptions = { }; class Service extends Dingus { - constructor(logger, db, options = {}) { + constructor(logger, db, options = {}, asyncLocalStorage) { super(logger, { ...defaultOptions, ...options }); common.setOptions(this, defaultOptions, options); + this.asyncLocalStorage = asyncLocalStorage; this.authenticator = new Authenticator(logger, db, options.authenticator); this._postRootAuth = this.authenticator[this.createRequiresAuth ? 'required' : 'optional'].bind(this.authenticator); @@ -59,7 +60,7 @@ class Service extends Dingus { default: return super.renderError(contentType, err); - } + } } @@ -88,6 +89,12 @@ class Service extends Dingus { async preHandler(req, res, ctx) { super.preHandler(req, res, ctx); Dingus.setEndBodyHandler(req, res, ctx, this._endHandler.bind(this)); + + const logObject = this.asyncLocalStorage.getStore(); + if (logObject) { + logObject.requestId = ctx.requestId; + delete ctx.requestId; + } } /** diff --git a/src/template/info-html.js b/src/template/info-html.js index 38ad53f..3fd62f9 100644 --- a/src/template/info-html.js +++ b/src/template/info-html.js @@ -1,6 +1,6 @@ 'use strict'; -const toDateString = (ts) => ts ? new Date(ts * 1000).toISOString() : 'never'; +const toDateString = (ts) => (ts && isFinite(ts)) ? new Date(ts * 1000).toISOString() : 'never'; module.exports = (ctx, details, pageTitle) => { const created = toDateString(details.created); diff --git a/src/template/report-html.js b/src/template/report-html.js index 7313daf..25f0619 100644 --- a/src/template/report-html.js +++ b/src/template/report-html.js @@ -1,6 +1,12 @@ 'use strict'; -const toDateString = (ts) => ts ? new Date(ts * 1000).toISOString() : 'never'; +const toDateString = (ts) => { + try { + return (ts && isFinite(ts)) ? (new Date(ts * 1000).toISOString()) : 'never'; + } catch (e) { + return `(whatever '${ts.toString()}' is)`; + } +}; module.exports = (ctx, links, pageTitle) => { return ` @@ -41,4 +47,4 @@ module.exports = (ctx, links, pageTitle) => { `; -}; \ No newline at end of file +}; diff --git a/test/src/service.js b/test/src/service.js index 4e1d017..5f4e223 100644 --- a/test/src/service.js +++ b/test/src/service.js @@ -12,15 +12,24 @@ const { ServeStaticFile } = require('../../src/errors'); describe('service', function () { - let service, logger, options; + let service, logger, options, asyncLocalStorage; let req, res, ctx; const dbStub = {}; beforeEach(function () { options = {}; - logger = {}; + logger = process.env['VERBOSE_TESTS'] ? console : { + debug: () => undefined, + warn: () => undefined, + info: () => undefined, + error: () => undefined, + log: () => undefined, + }; + asyncLocalStorage = { + getStore: () => ({}), + }; // logger = console; - service = new Service(logger, dbStub, options); + service = new Service(logger, dbStub, options, asyncLocalStorage); sinon.stub(service.manager); sinon.stub(service.authenticator); sinon.stub(service, 'setResponseType'); @@ -67,8 +76,8 @@ describe('service', function () { it('covers default', function () { const contentType = 'application/json'; const someData = { foo: 'bar', quux: 3 }; - ctx.rawBody = JSON.stringify(someData); - service.parseBody(contentType, ctx); + const rawBody = JSON.stringify(someData); + service.parseBody(contentType, ctx, rawBody); assert.deepStrictEqual(ctx.parsedBody, someData); }); }); // parseBody @@ -182,4 +191,4 @@ describe('service', function () { }); }); -}); \ No newline at end of file +}); -- 2.49.0