/* 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');
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, {
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);
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;
}
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;
}
// 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 {
'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
+};
};
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);
default:
return super.renderError(contentType, err);
- }
+ }
}
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;
+ }
}
/**
'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);
'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>
</main>
</body>
</html>`;
-};
\ No newline at end of file
+};
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');
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
});
});
-});
\ No newline at end of file
+});