support async log storate, fix lint issues
authorJustin Wind <justin.wind+git@gmail.com>
Fri, 23 Feb 2024 00:24:59 +0000 (16:24 -0800)
committerJustin Wind <justin.wind+git@gmail.com>
Fri, 23 Feb 2024 00:24:59 +0000 (16:24 -0800)
server.js
src/authenticator.js
src/db/errors.js
src/service.js
src/template/info-html.js
src/template/report-html.js
test/src/service.js

index 17302b76bf1e2d5a6fe3dc9947c6c155443e4bcc..979c1e0772b63fd4d0b8b04ef666e4d6b9372ae7 100644 (file)
--- 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);
index f3d33ecad073c7c0e981c26d7d2dbbfddaff8e43..c960078f3a624b9cb9d9780d4c404b7ee835e6ce 100644 (file)
@@ -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 {
index f13baba52d7d85bb39bf74acaa4311efd21565ac..4e5aa4a38e8898031d2634dc5bd090f6531c6e3b 100644 (file)
@@ -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
+};
index 97ed85e6e9e678fbae45cd0512135eb749930f9c..1596c001b73882d35b6d72554387811c0e93800e 100644 (file)
@@ -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;
+    }
   }
 
   /**
index 38ad53fea247be8c4a5575687257676e484f2a2c..3fd62f99feae4e2a8b3089719c761d4209bdaf7e 100644 (file)
@@ -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);
index 7313daf5dfc0898678225414c83fcf31dcb856c8..25f0619d638a5520b5810a1a5c442f80f23c3312 100644 (file)
@@ -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 `<!DOCTYPE html>
@@ -41,4 +47,4 @@ module.exports = (ctx, links, pageTitle) => {
     </main>
   </body>
 </html>`;
-};
\ No newline at end of file
+};
index 4e1d0174a06c881750bd1a7f896d4a6a07c7a3fb..5f4e223d2378c00087e30c3bc1a0ba8266310329 100644 (file)
@@ -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
+});