@axah/koa
Advanced tools
Comparing version 1.5.1 to 2.1.0
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { | ||
value: true | ||
}); | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
function createAuth( | ||
// eslint-disable-next-line @typescript-eslint/ban-types | ||
headers) { | ||
const userRoleHeader = headers['cp-axa-user-role']; | ||
const userIdHeader = headers['cp-axa-user-id']; | ||
if (!userRoleHeader) { | ||
throw new Error('Missing cp-axa-user-role header'); | ||
} | ||
if (!userIdHeader) { | ||
throw new Error('Missing cp-axa-user-id header'); | ||
} | ||
const userRoles = userRoleHeader | ||
.split(',') | ||
.map((r) => r.trim()) | ||
.filter((r) => r); | ||
if (userRoles.length === 0) { | ||
throw new Error('Empty cp-axa-user-role header'); | ||
} | ||
return { | ||
userId: userIdHeader, | ||
userRoles, | ||
hasRole(...roles) { | ||
return roles.some((role) => userRoles.indexOf(role) !== -1); | ||
}, | ||
}; | ||
} | ||
exports.default = createAuth; | ||
// eslint-disable-next-line import/no-unused-modules | ||
function createAuth(headers) { | ||
const userRoleHeader = headers['cp-axa-user-role']; | ||
const userIdHeader = headers['cp-axa-user-id']; | ||
if (!userRoleHeader) { | ||
throw new Error('Missing cp-axa-user-role header'); | ||
} | ||
if (!userIdHeader) { | ||
throw new Error('Missing cp-axa-user-id header'); | ||
} | ||
const userRoles = userRoleHeader.split(',').map(r => r.trim()).filter(r => r); | ||
if (userRoles.length === 0) { | ||
throw new Error('Empty cp-axa-user-role header'); | ||
} | ||
return { | ||
userId: userIdHeader, | ||
userRoles, | ||
hasRole(...roles) { | ||
return roles.some(role => userRoles.indexOf(role) !== -1); | ||
} | ||
}; | ||
} | ||
//# sourceMappingURL=index.js.map |
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { | ||
value: true | ||
}); | ||
var __importDefault = (this && this.__importDefault) || function (mod) { | ||
return (mod && mod.__esModule) ? mod : { "default": mod }; | ||
}; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
const koa_body_1 = __importDefault(require("koa-body")); | ||
const http_status_codes_1 = require("http-status-codes"); | ||
const auth_1 = __importDefault(require("./auth")); | ||
function createApiMethod({ allowedRoles, body: bodyValidator, query: queryValidator, params: paramsValidator, files: allowFiles = false, }, executor) { | ||
const parseBody = koa_body_1.default({ | ||
json: true, | ||
multipart: allowFiles, | ||
urlencoded: false, | ||
text: false, | ||
formLimit: '20mb', | ||
}); | ||
return async (ctx, next) => { | ||
// 1. Check authentication | ||
let auth; | ||
try { | ||
auth = auth_1.default(ctx.req.headers); | ||
} | ||
catch (e) { | ||
ctx.log.error(e, 'Failed to collect auth information'); | ||
ctx.status = http_status_codes_1.StatusCodes.FORBIDDEN; | ||
ctx.body = { error: http_status_codes_1.ReasonPhrases.FORBIDDEN }; | ||
return; | ||
} | ||
if (!auth.hasRole(...allowedRoles)) { | ||
ctx.log.error('User %s (%j) tried to access %s %s but does not have one of the required roles %j', auth.userId, auth.userRoles, ctx.method, ctx.path, allowedRoles); | ||
ctx.status = http_status_codes_1.StatusCodes.FORBIDDEN; | ||
ctx.body = { error: http_status_codes_1.ReasonPhrases.FORBIDDEN }; | ||
return; | ||
} | ||
// 2. parse body | ||
// eslint-disable-next-line @typescript-eslint/no-empty-function | ||
await parseBody(ctx, async () => { }); | ||
// 2b. validate body | ||
let bodyValidationResult; | ||
try { | ||
bodyValidationResult = bodyValidator.parse(Object.assign(Object.assign({}, ctx.request.body), (allowFiles ? ctx.request.files : {}))); | ||
} | ||
catch (e) { | ||
ctx.log.error(e, 'body is invalid'); | ||
ctx.status = http_status_codes_1.StatusCodes.NOT_ACCEPTABLE; | ||
ctx.body = { error: 'body is invalid' }; | ||
return; | ||
} | ||
let paramsValidationResult; | ||
try { | ||
paramsValidationResult = paramsValidator.parse(ctx.params || {}); | ||
} | ||
catch (e) { | ||
ctx.log.error(e, 'param is invalid'); | ||
ctx.status = http_status_codes_1.StatusCodes.NOT_ACCEPTABLE; | ||
ctx.body = { error: 'params is invalid' }; | ||
return; | ||
} | ||
let queryValidationResult; | ||
try { | ||
queryValidationResult = queryValidator.parse(ctx.query || {}); | ||
} | ||
catch (e) { | ||
ctx.log.error(e, 'query is invalid'); | ||
ctx.status = http_status_codes_1.StatusCodes.NOT_ACCEPTABLE; | ||
ctx.body = { error: 'query is invalid' }; | ||
return; | ||
} | ||
await executor({ | ||
body: bodyValidationResult, | ||
params: paramsValidationResult, | ||
query: queryValidationResult, | ||
auth, | ||
ctx, | ||
next, | ||
}); | ||
}; | ||
} | ||
exports.default = createApiMethod; | ||
var _koaBody = _interopRequireDefault(require("koa-body")); | ||
var _httpStatus = _interopRequireDefault(require("http-status")); | ||
var _auth = _interopRequireDefault(require("./auth")); | ||
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } | ||
function createApiMethod({ | ||
allowedRoles, | ||
body: bodyValidator, | ||
query: queryValidator, | ||
params: paramsValidator, | ||
files: allowFiles = false | ||
}, executor) { | ||
const parseBody = (0, _koaBody.default)({ | ||
json: true, | ||
multipart: allowFiles, | ||
urlencoded: false, | ||
text: false, | ||
formLimit: '20mb' | ||
}); | ||
return async (ctx, next) => { | ||
// 1. Check authentication | ||
let auth; | ||
try { | ||
auth = (0, _auth.default)(ctx.req.headers); | ||
} catch (e) { | ||
// $FlowFixMe ctx.log is not typed in koa... | ||
ctx.log.error(e, 'Failed to collect auth information'); | ||
ctx.status = _httpStatus.default.FORBIDDEN; | ||
ctx.body = { | ||
error: 'FORBIDDEN' | ||
}; | ||
return; | ||
} | ||
if (!auth.hasRole(...allowedRoles)) { | ||
// $FlowFixMe ctx.log is not typed in koa... | ||
ctx.log.error('User %s (%j) tried to access %s %s but does not have one of the required roles %j', auth.userId, auth.userRoles, ctx.method, ctx.path, allowedRoles); | ||
ctx.status = _httpStatus.default.FORBIDDEN; | ||
ctx.body = { | ||
error: 'FORBIDDEN' | ||
}; | ||
return; | ||
} // 2. parse body | ||
await parseBody(ctx, async () => {}); // 2b. validate body | ||
const bodyValidationResult = bodyValidator.validate({ ...ctx.request.body, | ||
...(allowFiles ? ctx.request.files : {}) | ||
}); | ||
if (bodyValidationResult.state === 'invalid') { | ||
// $FlowFixMe ctx.log is not typed in koa... | ||
ctx.log.error('Request body is invalid: %j', bodyValidationResult); | ||
ctx.status = _httpStatus.default.NOT_ACCEPTABLE; | ||
ctx.body = { | ||
error: 'body is invalid' | ||
}; | ||
return; | ||
} // 3. validate path params | ||
const paramsValidationResult = paramsValidator.validate(ctx.params || {}); | ||
if (paramsValidationResult.state === 'invalid') { | ||
// $FlowFixMe ctx.log is not typed in koa... | ||
ctx.log.error('param is invalid: %j', paramsValidationResult); | ||
ctx.status = _httpStatus.default.NOT_ACCEPTABLE; | ||
ctx.body = { | ||
error: 'params is invalid' | ||
}; | ||
return; | ||
} | ||
const queryValidationResult = queryValidator.validate(ctx.query || {}); | ||
if (queryValidationResult.state === 'invalid') { | ||
// $FlowFixMe ctx.log is not typed in koa... | ||
ctx.log.error('query is invalid: %j', queryValidationResult); | ||
ctx.status = _httpStatus.default.NOT_ACCEPTABLE; | ||
ctx.body = { | ||
error: 'query is invalid' | ||
}; | ||
return; | ||
} | ||
await executor({ | ||
body: bodyValidationResult.value, | ||
params: paramsValidationResult.value, | ||
query: queryValidationResult.value, | ||
auth, | ||
ctx, | ||
next | ||
}); | ||
}; | ||
} | ||
//# sourceMappingURL=index.js.map |
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { | ||
value: true | ||
}); | ||
exports.default = errorHandler; | ||
var _httpStatus = _interopRequireDefault(require("http-status")); | ||
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
const http_status_codes_1 = require("http-status-codes"); | ||
async function errorHandler(ctx, next) { | ||
try { | ||
await next(); | ||
if (!ctx.status) { | ||
// $FlowFixMe ctx.log is not typed in koa... | ||
ctx.log.error('No status set for request %s', ctx.path); | ||
try { | ||
await next(); | ||
if (!ctx.status) { | ||
ctx.log.error('No status set for request %s', ctx.path); | ||
} | ||
if (typeof ctx.body === 'undefined') { | ||
ctx.log.warn('No body set for request %s', ctx.path); | ||
} | ||
} | ||
if (typeof ctx.body === 'undefined') { | ||
// $FlowFixMe ctx.log is not typed in koa... | ||
ctx.log.warn('No body set for request %s', ctx.path); | ||
catch (e) { | ||
// no logging of errors required as the log middleware already does so... | ||
ctx.status = http_status_codes_1.StatusCodes.INTERNAL_SERVER_ERROR; | ||
ctx.body = { error: http_status_codes_1.ReasonPhrases.INTERNAL_SERVER_ERROR }; | ||
if (process.env.NODE_ENV !== 'production') { | ||
ctx.body.stack = e.stack; | ||
} | ||
} | ||
} catch (e) { | ||
// no logging of errors required as the log middleware already does so... | ||
ctx.status = _httpStatus.default.INTERNAL_SERVER_ERROR; | ||
ctx.body = { | ||
error: 'INTERNAL_SERVER_ERROR' | ||
}; | ||
if (process.env.NODE_ENV !== 'production') { | ||
// $FlowFixMe | ||
ctx.body.stack = e.stack; | ||
} | ||
} | ||
} | ||
} | ||
exports.default = errorHandler; | ||
//# sourceMappingURL=error-handler.js.map |
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { | ||
value: true | ||
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { | ||
if (k2 === undefined) k2 = k; | ||
Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } }); | ||
}) : (function(o, m, k, k2) { | ||
if (k2 === undefined) k2 = k; | ||
o[k2] = m[k]; | ||
})); | ||
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { | ||
Object.defineProperty(o, "default", { enumerable: true, value: v }); | ||
}) : function(o, v) { | ||
o["default"] = v; | ||
}); | ||
Object.defineProperty(exports, "errorHandler", { | ||
enumerable: true, | ||
get: function () { | ||
return _errorHandler.default; | ||
} | ||
}); | ||
Object.defineProperty(exports, "log", { | ||
enumerable: true, | ||
get: function () { | ||
return _log.default; | ||
} | ||
}); | ||
Object.defineProperty(exports, "validate", { | ||
enumerable: true, | ||
get: function () { | ||
return _validate.default; | ||
} | ||
}); | ||
Object.defineProperty(exports, "createApiMethod", { | ||
enumerable: true, | ||
get: function () { | ||
return _createApiMethod.default; | ||
} | ||
}); | ||
Object.defineProperty(exports, "createValidator", { | ||
enumerable: true, | ||
get: function () { | ||
return _createValidator.default; | ||
} | ||
}); | ||
Object.defineProperty(exports, "createOptionalValidator", { | ||
enumerable: true, | ||
get: function () { | ||
return _createValidator.createOptionalValidator; | ||
} | ||
}); | ||
Object.defineProperty(exports, "redact", { | ||
enumerable: true, | ||
get: function () { | ||
return _redact.default; | ||
} | ||
}); | ||
var _errorHandler = _interopRequireDefault(require("./error-handler")); | ||
var _log = _interopRequireDefault(require("./log")); | ||
var _validate = _interopRequireDefault(require("./create-api-method/validate")); | ||
var _createApiMethod = _interopRequireDefault(require("./create-api-method")); | ||
var _createValidator = _interopRequireWildcard(require("./create-api-method/validate/create-validator")); | ||
var _redact = _interopRequireDefault(require("./redact")); | ||
function _getRequireWildcardCache() { if (typeof WeakMap !== "function") return null; var cache = new WeakMap(); _getRequireWildcardCache = function () { return cache; }; return cache; } | ||
function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } if (obj === null || typeof obj !== "object" && typeof obj !== "function") { return { default: obj }; } var cache = _getRequireWildcardCache(); if (cache && cache.has(obj)) { return cache.get(obj); } var newObj = {}; var hasPropertyDescriptor = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) { var desc = hasPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : null; if (desc && (desc.get || desc.set)) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } newObj.default = obj; if (cache) { cache.set(obj, newObj); } return newObj; } | ||
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } | ||
var __importStar = (this && this.__importStar) || function (mod) { | ||
if (mod && mod.__esModule) return mod; | ||
var result = {}; | ||
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); | ||
__setModuleDefault(result, mod); | ||
return result; | ||
}; | ||
var __importDefault = (this && this.__importDefault) || function (mod) { | ||
return (mod && mod.__esModule) ? mod : { "default": mod }; | ||
}; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.redact = exports.createApiMethod = exports.validate = exports.log = exports.errorHandler = void 0; | ||
var error_handler_1 = require("./error-handler"); | ||
Object.defineProperty(exports, "errorHandler", { enumerable: true, get: function () { return __importDefault(error_handler_1).default; } }); | ||
var log_1 = require("./log"); | ||
Object.defineProperty(exports, "log", { enumerable: true, get: function () { return __importDefault(log_1).default; } }); | ||
exports.validate = __importStar(require("zod")); | ||
var create_api_method_1 = require("./create-api-method"); | ||
Object.defineProperty(exports, "createApiMethod", { enumerable: true, get: function () { return __importDefault(create_api_method_1).default; } }); | ||
var redact_1 = require("./redact"); | ||
Object.defineProperty(exports, "redact", { enumerable: true, get: function () { return __importDefault(redact_1).default; } }); | ||
//# sourceMappingURL=index.js.map |
233
lib/log.js
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { | ||
value: true | ||
}); | ||
exports.default = createLogMiddleware; | ||
var _v = _interopRequireDefault(require("uuid/v4")); | ||
var _pino = require("pino"); | ||
var _log = require("@axah/log"); | ||
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
const uuid_1 = require("uuid"); | ||
const pino_1 = require("pino"); | ||
const log_1 = require("@axah/log"); | ||
function parseJwt(token) { | ||
if (!token) { | ||
return null; | ||
} | ||
const payloadB64Url = token.split('.')[1]; | ||
const payloadB64 = payloadB64Url.replace(/-/g, '+').replace(/_/g, '/'); | ||
return JSON.parse(Buffer.from(payloadB64, 'base64').toString('utf8')); | ||
if (!token) { | ||
return null; | ||
} | ||
const payloadB64Url = token.split('.')[1]; | ||
const payloadB64 = payloadB64Url.replace(/-/g, '+').replace(/_/g, '/'); | ||
return JSON.parse(Buffer.from(payloadB64, 'base64').toString('utf8')); | ||
} | ||
function parseMessageIdFromHeaders(headers) { | ||
// cp-uuid from our loved CP | ||
return headers['cp-uuid'] // x-axa-msgid from ESG (API Gateway, for services) | ||
|| headers['x-axa-msgid'] // requestcorrelator from WAF (Web Application Firewall, for frontends) | ||
|| headers.requestcorrelator // custom uuid when nothing else is present | ||
|| `custom-${(0, _v.default)()}`; | ||
// cp-uuid from our loved CP | ||
return (headers['cp-uuid'] || | ||
// x-axa-msgid from ESG (API Gateway, for services) | ||
headers['x-axa-msgid'] || | ||
// requestcorrelator from WAF (Web Application Firewall, for frontends) | ||
headers.requestcorrelator || | ||
// custom uuid when nothing else is present | ||
`custom-${uuid_1.v4()}`); | ||
} | ||
function parseInitialMessageIdFromHeaders(headers) { | ||
// x-axa-initialmsgid from ESG (API Gateway, for services) | ||
return headers['x-axa-initialmsgid']; | ||
// x-axa-initialmsgid from ESG (API Gateway, for services) | ||
return headers['x-axa-initialmsgid']; | ||
} | ||
function parseSessionIdFromHeaders(headers) { | ||
// clientcorrelator from WAF (Web Application Firewall, for frontends) | ||
return headers.clientcorrelator; | ||
// clientcorrelator from WAF (Web Application Firewall, for frontends) | ||
return headers.clientcorrelator; | ||
} | ||
function parseAuthenticationInfoFromJwt(jwt) { | ||
const tokenPayload = parseJwt(jwt); | ||
return tokenPayload && { | ||
// x-axa-context V1 and MAAM AT V1 used to have a { value: string } object as sub, | ||
// V2s have a string sub | ||
user: typeof tokenPayload.sub === 'object' && tokenPayload.sub !== null ? tokenPayload.sub.value : tokenPayload.sub, | ||
initialUser: typeof tokenPayload.initialSub === 'object' && tokenPayload.initialSub !== null ? tokenPayload.initialSub.value : tokenPayload.initialSub, | ||
client: // client_id of x-axa-context v2 & MAAM v2 | ||
tokenPayload.client_id // initialSub of x-axa-context v1 | ||
|| tokenPayload.initialClientId // aud of MAAM access token v1 | ||
// NOTE: aud in MAAM v2 could be an array, but MAAM v2 as a client_id (preferred, see above) | ||
|| tokenPayload.aud | ||
}; | ||
const tokenPayload = parseJwt(jwt); | ||
return (tokenPayload && { | ||
// x-axa-context V1 and MAAM AT V1 used to have a { value: string } object as sub, | ||
// V2s have a string sub | ||
user: typeof tokenPayload.sub === 'object' && tokenPayload.sub !== null | ||
? tokenPayload.sub.value | ||
: tokenPayload.sub, | ||
initialUser: typeof tokenPayload.initialSub === 'object' && | ||
tokenPayload.initialSub !== null | ||
? tokenPayload.initialSub.value | ||
: tokenPayload.initialSub, | ||
client: | ||
// client_id of x-axa-context v2 & MAAM v2 | ||
tokenPayload.client_id || | ||
// initialSub of x-axa-context v1 | ||
tokenPayload.initialClientId || | ||
// aud of MAAM access token v1 | ||
// NOTE: aud in MAAM v2 could be an array, but MAAM v2 as a client_id (preferred, see above) | ||
tokenPayload.aud, | ||
}); | ||
} | ||
function parseAuthenticationInfoFromHeaders(headers) { | ||
// cp-axa-user-id / -role from our beloved api-gateway | ||
if (headers['cp-axa-user-id']) { | ||
return { | ||
user: headers['cp-axa-user-id'], | ||
role: headers['cp-axa-user-role'] | ||
}; | ||
} // authorization header (e.g. frontend inside WebView) | ||
if (headers.authorization && headers.authorization.startsWith('Bearer ')) { | ||
return parseAuthenticationInfoFromJwt(headers.authorization.substr(8)); | ||
} // x-axa-context from our beloved ESG | ||
if (headers['x-axa-context']) { | ||
return parseAuthenticationInfoFromJwt(headers['x-axa-context']); | ||
} | ||
return {}; | ||
// cp-axa-user-id / -role from our beloved api-gateway | ||
if (headers['cp-axa-user-id']) { | ||
return { | ||
user: headers['cp-axa-user-id'], | ||
role: headers['cp-axa-user-role'], | ||
}; | ||
} | ||
// authorization header (e.g. frontend inside WebView) | ||
if (headers.authorization && headers.authorization.startsWith('Bearer ')) { | ||
return parseAuthenticationInfoFromJwt(headers.authorization.substr(8)); | ||
} | ||
// x-axa-context from our beloved ESG | ||
if (headers['x-axa-context']) { | ||
return parseAuthenticationInfoFromJwt(headers['x-axa-context']); | ||
} | ||
return {}; | ||
} | ||
function parseMdcFromHeaders(logger, headers) { | ||
const messageId = parseMessageIdFromHeaders(headers); | ||
const initialMessageId = parseInitialMessageIdFromHeaders(headers); | ||
const sessionId = parseSessionIdFromHeaders(headers); | ||
let authInfo; | ||
try { | ||
authInfo = parseAuthenticationInfoFromHeaders(headers); | ||
} catch (e) { | ||
logger.error(e, 'Failed to parse auth info from headers'); | ||
authInfo = {}; | ||
} | ||
return { | ||
messageId, | ||
initialMessageId, | ||
sessionId, | ||
...authInfo | ||
}; | ||
const messageId = parseMessageIdFromHeaders(headers); | ||
const initialMessageId = parseInitialMessageIdFromHeaders(headers); | ||
const sessionId = parseSessionIdFromHeaders(headers); | ||
let authInfo; | ||
try { | ||
authInfo = parseAuthenticationInfoFromHeaders(headers); | ||
} | ||
catch (e) { | ||
logger.error(e, 'Failed to parse auth info from headers'); | ||
authInfo = {}; | ||
} | ||
return Object.assign({ messageId, | ||
initialMessageId, | ||
sessionId }, authInfo); | ||
} | ||
function createLogMiddleware(logger, logRequests = true) { | ||
return async function middleware(ctx, next) { | ||
const mdc = parseMdcFromHeaders(logger, ctx.req.headers); | ||
return (0, _log.runWithMdc)(mdc, async () => { | ||
// $FlowFixMe non-standard properties... | ||
const logDoNotUse = ctx.log = ctx.req.log = ctx.res.log = ctx.request.log = ctx.response.log = logger; // eslint-disable-line no-multi-assign, max-len | ||
const startTime = Date.now(); | ||
const requestLogger = logDoNotUse.child({ | ||
// $FlowFixMe flow doesn't like the pino typings too much... -.-" | ||
serializers: _pino.stdSerializers | ||
}); | ||
try { | ||
await next(); | ||
const responseTime = Date.now() - startTime; | ||
if (logRequests) { | ||
requestLogger.info({ | ||
responseTime, | ||
res: ctx.res, | ||
req: ctx.req | ||
}, 'Request to %s finished in %dms with status %s', ctx.path, responseTime, ctx.status); | ||
} | ||
} catch (e) { | ||
const responseTime = Date.now() - startTime; | ||
requestLogger.error({ | ||
responseTime, | ||
res: ctx.res, | ||
req: ctx.req, | ||
err: e | ||
}, 'Request to %s threw after %dms', ctx.path, responseTime); // re-throw here to let somebody else handle the error | ||
throw e; | ||
} | ||
}); | ||
}; | ||
} | ||
return async function middleware(ctx, next) { | ||
const mdc = parseMdcFromHeaders(logger, ctx.req.headers); | ||
return log_1.runWithMdc(mdc, async () => { | ||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment | ||
// @ts-ignore | ||
const logDoNotUse = (ctx.log = ctx.req.log = ctx.res.log = ctx.request.log = ctx.response.log = logger); // eslint-disable-line no-multi-assign, max-len | ||
const startTime = Date.now(); | ||
const requestLogger = logDoNotUse.child({ | ||
// $FlowFixMe flow doesn't like the pino typings too much... -.-" | ||
serializers: pino_1.stdSerializers, | ||
}); | ||
try { | ||
await next(); | ||
const responseTime = Date.now() - startTime; | ||
if (logRequests) { | ||
requestLogger.info({ | ||
responseTime, | ||
res: ctx.res, | ||
req: ctx.req, | ||
}, 'Request to %s finished in %dms with status %s', ctx.path, responseTime, ctx.status); | ||
} | ||
} | ||
catch (e) { | ||
const responseTime = Date.now() - startTime; | ||
requestLogger.error({ | ||
responseTime, | ||
res: ctx.res, | ||
req: ctx.req, | ||
err: e, | ||
}, 'Request to %s threw after %dms', ctx.path, responseTime); | ||
// re-throw here to let somebody else handle the error | ||
throw e; | ||
} | ||
}); | ||
}; | ||
} | ||
exports.default = createLogMiddleware; | ||
//# sourceMappingURL=log.js.map |
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { | ||
value: true | ||
}); | ||
exports.default = void 0; | ||
var _default = { | ||
paths: ['req.headers', 'res.headers'], | ||
censor: headers => Object.keys(headers).reduce((accum, key) => { | ||
switch (key) { | ||
case 'set-cookie': | ||
case 'cookie': | ||
case 'authorization': | ||
case 'x-axa-context': | ||
accum[key] = 'redacted'; // eslint-disable-line no-param-reassign | ||
break; | ||
default: | ||
accum[key] = headers[key]; | ||
// eslint-disable-line no-param-reassign | ||
} | ||
return accum; | ||
}, {}) | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.default = { | ||
paths: ['req.headers', 'res.headers'], | ||
censor: (headers) => Object.keys(headers).reduce((accum, key) => { | ||
switch (key) { | ||
case 'set-cookie': | ||
case 'cookie': | ||
case 'authorization': | ||
case 'x-axa-context': | ||
// eslint-disable-next-line no-param-reassign | ||
accum[key] = 'redacted'; | ||
break; | ||
default: | ||
// eslint-disable-next-line no-param-reassign | ||
accum[key] = headers[key]; | ||
} | ||
return accum; | ||
}, {}), | ||
}; | ||
exports.default = _default; | ||
//# sourceMappingURL=redact.js.map |
{ | ||
"name": "@axah/koa", | ||
"version": "1.5.1", | ||
"version": "2.1.0", | ||
"main": "lib/index.js", | ||
@@ -10,4 +10,4 @@ "license": "UNLICENSED", | ||
"scripts": { | ||
"dev": "nodemon -w src -e js --exec yarn build", | ||
"lint": "eslint . && flow check", | ||
"dev": "nodemon -w src -e ts --exec yarn build", | ||
"lint": " tsc --noEmit && eslint .", | ||
"prepublishOnly": "yarn build", | ||
@@ -17,32 +17,36 @@ "build": "yarn lint && yarn test && yarn clean && npm-run-all -p build:*", | ||
"clean": "rimraf lib", | ||
"build:babel": "babel src/ -d lib/ --ignore '**/*.test.js,**/__mocks__/**'", | ||
"build:flow": "flow-copy-source -i '**/*.test.js' -i '**/__mocks__/**' src lib/", | ||
"build:typescript": "tsc --build tsconfig.build.json", | ||
"test": "jest", | ||
"ci": "yarn build && yarn flow stop" | ||
"ci": "yarn build" | ||
}, | ||
"devDependencies": { | ||
"@babel/cli": "^7.8.4", | ||
"@babel/core": "^7.9.0", | ||
"@babel/preset-env": "^7.9.5", | ||
"@babel/preset-flow": "^7.9.0", | ||
"babel-eslint": "^10.1.0", | ||
"eslint": "^6.8.0", | ||
"eslint-config-airbnb-base": "^14.1.0", | ||
"eslint-plugin-flowtype": "^4.7.0", | ||
"eslint-plugin-import": "^2.20.2", | ||
"flow-bin": "0.123.0", | ||
"flow-copy-source": "^2.0.9", | ||
"flow-typed": "^3.1.0", | ||
"jest": "^25.5.0", | ||
"koa": "^2.11.0", | ||
"nodemon": "^2.0.3", | ||
"@shopify/jest-koa-mocks": "^2.2.3", | ||
"@types/jest": "^26.0.13", | ||
"@types/koa": "^2.11.4", | ||
"@types/node": "^14.10.1", | ||
"@types/pino": "^6.3.2", | ||
"@types/uuid": "^8.3.0", | ||
"@typescript-eslint/eslint-plugin": "^4.1.0", | ||
"@typescript-eslint/parser": "^4.1.0", | ||
"eslint": "^7.9.0", | ||
"eslint-config-airbnb-base": "^14.2.0", | ||
"eslint-config-prettier": "^6.11.0", | ||
"eslint-plugin-import": "^2.22.0", | ||
"eslint-plugin-prettier": "^3.1.4", | ||
"flowgen": "^1.11.0", | ||
"jest": "^26.4.2", | ||
"koa": "^2.13.0", | ||
"nodemon": "^2.0.4", | ||
"npm-run-all": "^4.1.5", | ||
"pino": "^5.17.0" | ||
"pino": "^6.7.0", | ||
"prettier": "^2.1.1", | ||
"ts-jest": "^26.3.0", | ||
"typescript": "^4.0.2" | ||
}, | ||
"dependencies": { | ||
"@axah/log": "^1.1.0", | ||
"http-status": "^1.4.2", | ||
"koa-body": "^4.1.1", | ||
"uuid": "^3.4.0", | ||
"valid-url": "^1.0.9" | ||
"http-status-codes": "^2.1.4", | ||
"koa-body": "^4.2.0", | ||
"uuid": "^8.3.1", | ||
"zod": "2.0.0-beta.5" | ||
}, | ||
@@ -49,0 +53,0 @@ "peerDependencies": { |
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
Network access
Supply chain riskThis module accesses the network.
Found 1 instance in 1 package
Network access
Supply chain riskThis module accesses the network.
Found 1 instance in 1 package
New author
Supply chain riskA new npm collaborator published a version of the package for the first time. New collaborators are usually benign additions to a project, but do indicate a change to the security surface area of a package.
Found 1 instance in 1 package
Filesystem access
Supply chain riskAccesses the file system, and could potentially read sensitive data.
Found 1 instance in 1 package
2
25736
22
20
356
+ Addedhttp-status-codes@^2.1.4
+ Addedzod@2.0.0-beta.5
+ Addedhttp-status-codes@2.3.0(transitive)
+ Addeduuid@8.3.2(transitive)
+ Addedzod@2.0.0-beta.5(transitive)
- Removedhttp-status@^1.4.2
- Removedvalid-url@^1.0.9
- Removedhttp-status@1.8.1(transitive)
- Removeduuid@3.4.0(transitive)
- Removedvalid-url@1.0.9(transitive)
Updatedkoa-body@^4.2.0
Updateduuid@^8.3.1