From 7df7d57e923de78108c2d32279217dacca31b004 Mon Sep 17 00:00:00 2001 From: Jie Hao Kwa Date: Mon, 9 Nov 2020 14:06:38 +0800 Subject: [PATCH 01/10] chore: install dependencies needed for logger component This commit installs the necessary dependencies for the logger component. The reason we need a separate logger, which logs to CloudWatch directly, is because the EC2 instance that our server is running on might batches log statements before sending them to CloudWatch. Logs might fail to get sent if the EC2 cloudwatch daemon is corrupted or fails in some way. This external logger will send logs instantly to CloudWatch so we have a fallback source of logs. We chose to use the Winston logger as we have had success using it for the homer project. this commit also installs the aws sdk as we need to retrieve the EC2 instance id to log to the correct cloudwatch log group. --- package-lock.json | 821 +++++++++++++++++++++++++++++++++++++++++++++- package.json | 6 +- 2 files changed, 819 insertions(+), 8 deletions(-) diff --git a/package-lock.json b/package-lock.json index ff43ccfb3..a894f7e25 100644 --- a/package-lock.json +++ b/package-lock.json @@ -59,6 +59,16 @@ } } }, + "@dabh/diagnostics": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/@dabh/diagnostics/-/diagnostics-2.0.2.tgz", + "integrity": "sha512-+A1YivoVDNNVCdfozHSR8v/jyuuLTMXwjWuxPFlFlUapXoGc+Gj9mDlTDDfrwl7rXCl2tNZ0kE8sIBO6YOn96Q==", + "requires": { + "colorspace": "1.1.x", + "enabled": "2.0.x", + "kuler": "^2.0.0" + } + }, "@types/node": { "version": "12.12.21", "resolved": "https://registry.npmjs.org/@types/node/-/node-12.12.21.tgz", @@ -75,6 +85,14 @@ "negotiator": "0.6.2" } }, + "agent-base": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-4.3.0.tgz", + "integrity": "sha512-salcGninV0nPrwpGNn4VTXBb1SOuXQBiqbrNXoeizJsHrsL6ERFM2Ne3JUSBWRE6aeNJI2ROP/WEEIDUiDe3cg==", + "requires": { + "es6-promisify": "^5.0.0" + } + }, "ansi-escapes": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-3.2.0.tgz", @@ -139,12 +157,55 @@ "integrity": "sha1-WWZ/QfrdTyDMvCu5a41Pf3jsA2c=", "dev": true }, + "ast-types": { + "version": "0.14.2", + "resolved": "https://registry.npmjs.org/ast-types/-/ast-types-0.14.2.tgz", + "integrity": "sha512-O0yuUDnZeQDL+ncNGlJ78BiO4jnYI3bvMsD5prT0/nsgijG/LpNBIr63gTjVTNsiGkgQhiyCShTgxt8oXOrklA==", + "requires": { + "tslib": "^2.0.1" + }, + "dependencies": { + "tslib": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.0.3.tgz", + "integrity": "sha512-uZtkfKblCEQtZKBF6EBXVZeQNl82yqtDQdv+eck8u7tdPxjLu2/lp5/uPW+um2tpuxINHWy3GhiccY7QgEaVHQ==" + } + } + }, + "async": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/async/-/async-3.2.0.tgz", + "integrity": "sha512-TR2mEZFVOj2pLStYxLht7TyfuRzaydfpxr3k9RpHIzMgw7A64dzsdqCxH1WJyQdoe8T10nDXd9wnEigmiuHIZw==" + }, "atob": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/atob/-/atob-2.1.2.tgz", "integrity": "sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg==", "dev": true }, + "aws-sdk": { + "version": "2.787.0", + "resolved": "https://registry.npmjs.org/aws-sdk/-/aws-sdk-2.787.0.tgz", + "integrity": "sha512-3WlUdWqUB8Vhdvj/7TENr/7SEmQzxmnHxOJ8l2WjZbcMRSuI0/9Ym4p1TC3hf21VDVDhkdGlw60QqpZQ1qb+Mg==", + "requires": { + "buffer": "4.9.2", + "events": "1.1.1", + "ieee754": "1.1.13", + "jmespath": "0.15.0", + "querystring": "0.2.0", + "sax": "1.2.1", + "url": "0.10.3", + "uuid": "3.3.2", + "xml2js": "0.4.19" + }, + "dependencies": { + "uuid": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.3.2.tgz", + "integrity": "sha512-yXJmeNaw3DnnKAOKJE51sL/ZaYfWJRl1pK9dr19YFCu0ObS231AB1/LbqTKRAQ5kw8A90rA6fr4riOUpTZvQZA==" + } + } + }, "axios": { "version": "0.19.0", "resolved": "https://registry.npmjs.org/axios/-/axios-0.19.0.tgz", @@ -231,6 +292,11 @@ "resolved": "https://registry.npmjs.org/base-64/-/base-64-0.1.0.tgz", "integrity": "sha1-eAqZyE59YAJgNhURxId2E78k9rs=" }, + "base64-js": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.3.1.tgz", + "integrity": "sha512-mLQ4i2QO1ytvGWFWmcngKO//JXAQueZvwEKtjgQFM4jIK0kU+ytMfplL8j+n5mspOfjHwoAg+9yhb7BwAHm36g==" + }, "basic-auth": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/basic-auth/-/basic-auth-2.0.1.tgz", @@ -329,6 +395,16 @@ } } }, + "buffer": { + "version": "4.9.2", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-4.9.2.tgz", + "integrity": "sha512-xq+q3SRMOxGivLhBNaUdC64hDTQwejJ+H0T/NB1XMtTVEwNTrfFF3gAxiyW0Bu/xWEGhjVKgUcMhCrUy2+uCWg==", + "requires": { + "base64-js": "^1.0.2", + "ieee754": "^1.1.4", + "isarray": "^1.0.0" + } + }, "buffer-equal-constant-time": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz", @@ -453,6 +529,11 @@ "integrity": "sha1-/xnt6Kml5XkyQUewwR8PvLq+1jk=", "dev": true }, + "co": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", + "integrity": "sha1-bqa989hTrlTMuOR7+gvz+QMfsYQ=" + }, "collection-visit": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/collection-visit/-/collection-visit-1.0.0.tgz", @@ -463,11 +544,19 @@ "object-visit": "^1.0.0" } }, + "color": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/color/-/color-3.0.0.tgz", + "integrity": "sha512-jCpd5+s0s0t7p3pHQKpnJ0TpQKKdleP71LWcA0aqiljpiuAkOSUFN/dyH8ZwF0hRmFlrIuRhufds1QyEP9EB+w==", + "requires": { + "color-convert": "^1.9.1", + "color-string": "^1.5.2" + } + }, "color-convert": { "version": "1.9.3", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", - "dev": true, "requires": { "color-name": "1.1.3" } @@ -475,8 +564,30 @@ "color-name": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", - "dev": true + "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=" + }, + "color-string": { + "version": "1.5.4", + "resolved": "https://registry.npmjs.org/color-string/-/color-string-1.5.4.tgz", + "integrity": "sha512-57yF5yt8Xa3czSEW1jfQDE79Idk0+AkN/4KWad6tbdxUmAs3MvjxlWSWD4deYytcRfoZ9nhKyFl1kj5tBvidbw==", + "requires": { + "color-name": "^1.0.0", + "simple-swizzle": "^0.2.2" + } + }, + "colors": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/colors/-/colors-1.4.0.tgz", + "integrity": "sha512-a+UqTh4kgZg/SlGvfbzDHpgRu7AAQOmmqRHJnxhRZICKFUT91brVhNNt58CMWU9PsBbv3PDCZUHbVxuDiH2mtA==" + }, + "colorspace": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/colorspace/-/colorspace-1.1.2.tgz", + "integrity": "sha512-vt+OoIP2d76xLhjwbBaucYlNSpPsrJWPlBTtwCpQKIu6/CSMutyzX93O/Do0qzpH3YoHEes8YEFXyZ797rEhzQ==", + "requires": { + "color": "3.0.x", + "text-hex": "1.0.x" + } }, "commitizen": { "version": "4.0.3", @@ -584,6 +695,11 @@ "dev": true, "optional": true }, + "core-util-is": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", + "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=" + }, "cors": { "version": "2.8.5", "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.5.tgz", @@ -642,6 +758,11 @@ "word-wrap": "^1.0.3" } }, + "data-uri-to-buffer": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/data-uri-to-buffer/-/data-uri-to-buffer-1.2.0.tgz", + "integrity": "sha512-vKQ9DTQPN1FLYiiEEOQ6IBGFqvjCa5rSK3cWMy/Nespm5d/x3dGFT9UBZnkLxCwua/IXBi2TYnwTEpsOvhC4UQ==" + }, "debug": { "version": "2.6.9", "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", @@ -661,6 +782,11 @@ "integrity": "sha1-JJXduvbrh0q7Dhvp3yLS5aVEMmw=", "dev": true }, + "deep-is": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.3.tgz", + "integrity": "sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ=" + }, "define-property": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/define-property/-/define-property-2.0.2.tgz", @@ -702,6 +828,23 @@ } } }, + "degenerator": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/degenerator/-/degenerator-1.0.4.tgz", + "integrity": "sha1-/PSQo37OJmRk2cxDGrmMWBnO0JU=", + "requires": { + "ast-types": "0.x.x", + "escodegen": "1.x.x", + "esprima": "3.x.x" + }, + "dependencies": { + "esprima": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-3.1.3.tgz", + "integrity": "sha1-/cpRzuYTOJXjyI1TXOSdv/YqRjM=" + } + } + }, "depd": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", @@ -742,6 +885,11 @@ "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", "integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=" }, + "enabled": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/enabled/-/enabled-2.0.0.tgz", + "integrity": "sha512-AKrN98kuwOzMIdAizXGI86UFBoo26CL21UM763y1h/GMSJ4/OHU9k2YlsmBpyScFo/wbLzWQJBMCW4+IO3/+OQ==" + }, "encodeurl": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", @@ -757,6 +905,19 @@ "is-arrayish": "^0.2.1" } }, + "es6-promise": { + "version": "4.2.8", + "resolved": "https://registry.npmjs.org/es6-promise/-/es6-promise-4.2.8.tgz", + "integrity": "sha512-HJDGx5daxeIvxdBxvG2cb9g4tEvwIk3i8+nhX0yGrYmZUzbkdg8QbDevheDB8gd0//uPj4c1EQua8Q+MViT0/w==" + }, + "es6-promisify": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/es6-promisify/-/es6-promisify-5.0.0.tgz", + "integrity": "sha1-UQnWLz5W6pZ8S2NQWu8IKRyKUgM=", + "requires": { + "es6-promise": "^4.0.3" + } + }, "escape-html": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", @@ -768,16 +929,51 @@ "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", "dev": true }, + "escodegen": { + "version": "1.14.3", + "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-1.14.3.tgz", + "integrity": "sha512-qFcX0XJkdg+PB3xjZZG/wKSuT1PnQWx57+TVSjIMmILd2yC/6ByYElPwJnslDsuWuSAp4AwJGumarAAmJch5Kw==", + "requires": { + "esprima": "^4.0.1", + "estraverse": "^4.2.0", + "esutils": "^2.0.2", + "optionator": "^0.8.1", + "source-map": "~0.6.1" + }, + "dependencies": { + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "optional": true + } + } + }, "esprima": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==" }, + "estraverse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", + "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==" + }, + "esutils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==" + }, "etag": { "version": "1.8.1", "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", "integrity": "sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc=" }, + "events": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/events/-/events-1.1.1.tgz", + "integrity": "sha1-nr23Y1rQmccNzEwqH1AEKI6L2SQ=" + }, "expand-brackets": { "version": "2.1.4", "resolved": "https://registry.npmjs.org/expand-brackets/-/expand-brackets-2.1.4.tgz", @@ -902,6 +1098,11 @@ } } }, + "extend": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", + "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==" + }, "extend-shallow": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-3.0.2.tgz", @@ -999,6 +1200,21 @@ } } }, + "fast-levenshtein": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", + "integrity": "sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=" + }, + "fast-safe-stringify": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/fast-safe-stringify/-/fast-safe-stringify-2.0.7.tgz", + "integrity": "sha512-Utm6CdzT+6xsDk2m8S6uL8VHxNwI6Jub+e9NYTcAms28T84pTa25GJQV9j0CY0N1rM8hK4x6grpF2BQf+2qwVA==" + }, + "fecha": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/fecha/-/fecha-4.2.0.tgz", + "integrity": "sha512-aN3pcx/DSmtyoovUudctc8+6Hl4T+hI9GBBHLjA76jdZl7+b1sgh5g4k+u/GL3dTy1/pnYzKp69FpJ0OicE3Wg==" + }, "figures": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/figures/-/figures-2.0.0.tgz", @@ -1008,6 +1224,11 @@ "escape-string-regexp": "^1.0.5" } }, + "file-uri-to-path": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz", + "integrity": "sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==" + }, "fill-range": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz", @@ -1073,6 +1294,11 @@ "resolve-dir": "^1.0.1" } }, + "fn.name": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/fn.name/-/fn.name-1.1.0.tgz", + "integrity": "sha512-GRnmB5gPyJpAhTQdSZTSp9uaPSvl09KoYcMQtsB9rQoOmzs9dH6ffeccH+Z+cv6P68Hu5bC6JjRh4Ah/mHSNRw==" + }, "follow-redirects": { "version": "1.5.10", "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.5.10.tgz", @@ -1133,6 +1359,75 @@ "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", "dev": true }, + "ftp": { + "version": "0.3.10", + "resolved": "https://registry.npmjs.org/ftp/-/ftp-0.3.10.tgz", + "integrity": "sha1-kZfYYa2BQvPmPVqDv+TFn3MwiF0=", + "requires": { + "readable-stream": "1.1.x", + "xregexp": "2.0.0" + }, + "dependencies": { + "isarray": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", + "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=" + }, + "readable-stream": { + "version": "1.1.14", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.1.14.tgz", + "integrity": "sha1-fPTFTvZI44EwhMY23SB54WbAgdk=", + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.1", + "isarray": "0.0.1", + "string_decoder": "~0.10.x" + } + }, + "string_decoder": { + "version": "0.10.31", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", + "integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ=" + } + } + }, + "get-uri": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/get-uri/-/get-uri-2.0.4.tgz", + "integrity": "sha512-v7LT/s8kVjs+Tx0ykk1I+H/rbpzkHvuIq87LmeXptcf5sNWm9uQiwjNAt94SJPA1zOlCntmnOlJvVWKmzsxG8Q==", + "requires": { + "data-uri-to-buffer": "1", + "debug": "2", + "extend": "~3.0.2", + "file-uri-to-path": "1", + "ftp": "~0.3.10", + "readable-stream": "2" + }, + "dependencies": { + "readable-stream": { + "version": "2.3.7", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", + "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "requires": { + "safe-buffer": "~5.1.0" + } + } + } + }, "get-value": { "version": "2.0.6", "resolved": "https://registry.npmjs.org/get-value/-/get-value-2.0.6.tgz", @@ -1257,6 +1552,49 @@ "statuses": ">= 1.4.0 < 2" } }, + "http-proxy-agent": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-2.1.0.tgz", + "integrity": "sha512-qwHbBLV7WviBl0rQsOzH6o5lwyOIvwp/BdFnvVxXORldu5TmjFfjzBcWUWS5kWAZhmv+JtiDhSuQCp4sBfbIgg==", + "requires": { + "agent-base": "4", + "debug": "3.1.0" + }, + "dependencies": { + "debug": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", + "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", + "requires": { + "ms": "2.0.0" + } + } + } + }, + "https-proxy-agent": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-3.0.1.tgz", + "integrity": "sha512-+ML2Rbh6DAuee7d07tYGEKOEi2voWPUGan+ExdPbPW6Z3svq+JCqr0v8WmKPOkz1vOVykPCBSuobe7G8GJUtVg==", + "requires": { + "agent-base": "^4.3.0", + "debug": "^3.1.0" + }, + "dependencies": { + "debug": { + "version": "3.2.6", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz", + "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==", + "requires": { + "ms": "^2.1.1" + } + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + } + } + }, "iconv-lite": { "version": "0.4.24", "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", @@ -1265,6 +1603,11 @@ "safer-buffer": ">= 2.1.2 < 3" } }, + "ieee754": { + "version": "1.1.13", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.1.13.tgz", + "integrity": "sha512-4vf7I2LYV/HaWerSo3XmlMkp5eZ83i+/CDluXi/IGTs/O1sejBNhTtnxzmRZfvOUqj7lZjqHkeTvpgSFDlWZTg==" + }, "import-fresh": { "version": "3.2.1", "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.2.1.tgz", @@ -1333,6 +1676,11 @@ "integrity": "sha512-mT34yGKMNceBQUoVn7iCDKDntA7SC6gycMAWzGx1z/CMCTV7b2AAtXlo3nRyHZ1FelRkQbQjprHSYGwzLtkVbw==", "dev": true }, + "ip": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/ip/-/ip-1.1.5.tgz", + "integrity": "sha1-vd7XARQpCCjAoDnnLvJfWq7ENUo=" + }, "ipaddr.js": { "version": "1.9.0", "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.0.tgz", @@ -1496,6 +1844,11 @@ "integrity": "sha1-eaKp7OfwlugPNtKy87wWwf9L8/o=", "dev": true }, + "is-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.0.tgz", + "integrity": "sha512-XCoy+WlUr7d1+Z8GgSuXmpuUFC9fOhRXglJMx+dwLKTkL44Cjd4W1Z5P+BQZpr+cR93aGP4S/s7Ftw6Nd/kiEw==" + }, "is-utf8": { "version": "0.2.1", "resolved": "https://registry.npmjs.org/is-utf8/-/is-utf8-0.2.1.tgz", @@ -1511,8 +1864,7 @@ "isarray": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", - "dev": true + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=" }, "isexe": { "version": "2.0.0", @@ -1526,6 +1878,11 @@ "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=", "dev": true }, + "jmespath": { + "version": "0.15.0", + "resolved": "https://registry.npmjs.org/jmespath/-/jmespath-0.15.0.tgz", + "integrity": "sha1-o/Iiqarp+Wb10nx5ZRDigJF2Qhc=" + }, "js-base64": { "version": "2.5.1", "resolved": "https://registry.npmjs.org/js-base64/-/js-base64-2.5.1.tgz", @@ -1605,11 +1962,35 @@ "integrity": "sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA==", "dev": true }, + "kuler": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/kuler/-/kuler-2.0.0.tgz", + "integrity": "sha512-Xq9nH7KlWZmXAtodXDDRE7vs6DU1gTU8zYDHDiWLSip45Egwq3plLHzPn27NgvzL2r1LMPC1vdqh98sQxtqj4A==" + }, + "levn": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.3.0.tgz", + "integrity": "sha1-OwmSTt+fCDwEkP3UwLxEIeBHZO4=", + "requires": { + "prelude-ls": "~1.1.2", + "type-check": "~0.3.2" + } + }, "lodash": { "version": "4.17.19", "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.19.tgz", "integrity": "sha512-JNvd8XER9GQX0v2qJgsaN/mzFCNA5BRe/j8JN9d+tWyGLSodKQHKFicdwNYzWwI3wjRnaKPsGj1XkBjx/F96DQ==" }, + "lodash.assign": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/lodash.assign/-/lodash.assign-4.2.0.tgz", + "integrity": "sha1-DZnzzNem0mHRm9rrkkUAXShYCOc=" + }, + "lodash.find": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/lodash.find/-/lodash.find-4.6.0.tgz", + "integrity": "sha1-ywcE1Hq3F4n/oN6Ll92Sb7iLE7E=" + }, "lodash.includes": { "version": "4.3.0", "resolved": "https://registry.npmjs.org/lodash.includes/-/lodash.includes-4.3.0.tgz", @@ -1620,6 +2001,16 @@ "resolved": "https://registry.npmjs.org/lodash.isboolean/-/lodash.isboolean-3.0.3.tgz", "integrity": "sha1-bC4XHbKiV82WgC/UOwGyDV9YcPY=" }, + "lodash.isempty": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/lodash.isempty/-/lodash.isempty-4.4.0.tgz", + "integrity": "sha1-b4bL7di+TsmHvpqvM8loTbGzHn4=" + }, + "lodash.iserror": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/lodash.iserror/-/lodash.iserror-3.1.1.tgz", + "integrity": "sha1-KXuaBfq2cUvCRE18wZ0dfES17Ow=" + }, "lodash.isinteger": { "version": "4.0.4", "resolved": "https://registry.npmjs.org/lodash.isinteger/-/lodash.isinteger-4.0.4.tgz", @@ -1651,12 +2042,39 @@ "resolved": "https://registry.npmjs.org/lodash.once/-/lodash.once-4.1.1.tgz", "integrity": "sha1-DdOXEhPHxW34gJd9UEyI+0cal6w=" }, + "logform": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/logform/-/logform-2.2.0.tgz", + "integrity": "sha512-N0qPlqfypFx7UHNn4B3lzS/b0uLqt2hmuoa+PpuXNYgozdJYAyauF5Ky0BWVjrxDlMWiT3qN4zPq3vVAfZy7Yg==", + "requires": { + "colors": "^1.2.1", + "fast-safe-stringify": "^2.0.4", + "fecha": "^4.2.0", + "ms": "^2.1.1", + "triple-beam": "^1.3.0" + }, + "dependencies": { + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + } + } + }, "longest": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/longest/-/longest-2.0.1.tgz", "integrity": "sha1-eB4YMpaqlPbU2RbcM10NF676I/g=", "dev": true }, + "lru-cache": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", + "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", + "requires": { + "yallist": "^3.0.2" + } + }, "map-cache": { "version": "0.2.2", "resolved": "https://registry.npmjs.org/map-cache/-/map-cache-0.2.2.tgz", @@ -1774,6 +2192,19 @@ } } }, + "moment": { + "version": "2.29.1", + "resolved": "https://registry.npmjs.org/moment/-/moment-2.29.1.tgz", + "integrity": "sha512-kHmoybcPV8Sqy59DwNDY3Jefr64lK/by/da0ViFcuA4DH0vQg5Q6Ze5VimxkfQNSC+Mls/Kx53s7TjP1RhFEDQ==" + }, + "moment-timezone": { + "version": "0.5.31", + "resolved": "https://registry.npmjs.org/moment-timezone/-/moment-timezone-0.5.31.tgz", + "integrity": "sha512-+GgHNg8xRhMXfEbv81iDtrVeTcWt0kWmTEY1XQK14dICTXnWJnT0dxdlPspwqF3keKMVPXwayEsk1DI0AA/jdA==", + "requires": { + "moment": ">= 2.9.0" + } + }, "morgan": { "version": "1.9.1", "resolved": "https://registry.npmjs.org/morgan/-/morgan-1.9.1.tgz", @@ -1821,6 +2252,11 @@ "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.2.tgz", "integrity": "sha512-hZXc7K2e+PgeI1eDBe/10Ard4ekbfrrqG8Ep+8Jmf4JID2bNg7NvCPOZN+kfF574pFQI7mum2AUqDidoKqcTOw==" }, + "netmask": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/netmask/-/netmask-1.0.6.tgz", + "integrity": "sha1-ICl+idhvb2QA8lDZ9Pa0wZRfzTU=" + }, "object-assign": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", @@ -1903,6 +2339,14 @@ "wrappy": "1" } }, + "one-time": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/one-time/-/one-time-1.0.0.tgz", + "integrity": "sha512-5DXOiRKwuSEcQ/l0kGCF6Q3jcADFv5tSmRaJck/OqkVFcOzutB134KRSfF0xDrL39MNnqxbHBbUUcjZIhTgb2g==", + "requires": { + "fn.name": "1.x.x" + } + }, "onetime": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/onetime/-/onetime-2.0.1.tgz", @@ -1912,12 +2356,67 @@ "mimic-fn": "^1.0.0" } }, + "optionator": { + "version": "0.8.3", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.8.3.tgz", + "integrity": "sha512-+IW9pACdk3XWmmTXG8m3upGUJst5XRGzxMRjXzAuJ1XnIFNvfhjjIuYkDvysnPQ7qzqVzLt78BCruntqRhWQbA==", + "requires": { + "deep-is": "~0.1.3", + "fast-levenshtein": "~2.0.6", + "levn": "~0.3.0", + "prelude-ls": "~1.1.2", + "type-check": "~0.3.2", + "word-wrap": "~1.2.3" + } + }, "os-tmpdir": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", "integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=", "dev": true }, + "pac-proxy-agent": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/pac-proxy-agent/-/pac-proxy-agent-3.0.1.tgz", + "integrity": "sha512-44DUg21G/liUZ48dJpUSjZnFfZro/0K5JTyFYLBcmh9+T6Ooi4/i4efwUiEy0+4oQusCBqWdhv16XohIj1GqnQ==", + "requires": { + "agent-base": "^4.2.0", + "debug": "^4.1.1", + "get-uri": "^2.0.0", + "http-proxy-agent": "^2.1.0", + "https-proxy-agent": "^3.0.0", + "pac-resolver": "^3.0.0", + "raw-body": "^2.2.0", + "socks-proxy-agent": "^4.0.1" + }, + "dependencies": { + "debug": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.2.0.tgz", + "integrity": "sha512-IX2ncY78vDTjZMFUdmsvIRFY2Cf4FnD0wRs+nQwJU8Lu99/tPFdb0VybiiMTPe3I6rQmwsqQqRBvxU+bZ/I8sg==", + "requires": { + "ms": "2.1.2" + } + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + } + } + }, + "pac-resolver": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pac-resolver/-/pac-resolver-3.0.0.tgz", + "integrity": "sha512-tcc38bsjuE3XZ5+4vP96OfhOugrX+JcnpUbhfuc4LuXBLQhoTthOstZeoQJBDnQUDYzYmdImKsbz0xSl1/9qeA==", + "requires": { + "co": "^4.6.0", + "degenerator": "^1.0.4", + "ip": "^1.1.5", + "netmask": "^1.0.6", + "thunkify": "^2.1.2" + } + }, "parent-module": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", @@ -1979,6 +2478,16 @@ "integrity": "sha1-AerA/jta9xoqbAL+q7jB/vfgDqs=", "dev": true }, + "prelude-ls": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.1.2.tgz", + "integrity": "sha1-IZMqVJ9eUv/ZqCf1cOBL5iqX2lQ=" + }, + "process-nextick-args": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", + "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==" + }, "proxy-addr": { "version": "2.0.5", "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.5.tgz", @@ -1988,6 +2497,46 @@ "ipaddr.js": "1.9.0" } }, + "proxy-agent": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/proxy-agent/-/proxy-agent-3.1.1.tgz", + "integrity": "sha512-WudaR0eTsDx33O3EJE16PjBRZWcX8GqCEeERw1W3hZJgH/F2a46g7jty6UGty6NeJ4CKQy8ds2CJPMiyeqaTvw==", + "requires": { + "agent-base": "^4.2.0", + "debug": "4", + "http-proxy-agent": "^2.1.0", + "https-proxy-agent": "^3.0.0", + "lru-cache": "^5.1.1", + "pac-proxy-agent": "^3.0.1", + "proxy-from-env": "^1.0.0", + "socks-proxy-agent": "^4.0.1" + }, + "dependencies": { + "debug": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.2.0.tgz", + "integrity": "sha512-IX2ncY78vDTjZMFUdmsvIRFY2Cf4FnD0wRs+nQwJU8Lu99/tPFdb0VybiiMTPe3I6rQmwsqQqRBvxU+bZ/I8sg==", + "requires": { + "ms": "2.1.2" + } + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + } + } + }, + "proxy-from-env": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", + "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==" + }, + "punycode": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.3.2.tgz", + "integrity": "sha1-llOgNvt8HuQjQvIyXM7v6jkmxI0=" + }, "qs": { "version": "6.5.2", "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.2.tgz", @@ -2003,6 +2552,11 @@ "strict-uri-encode": "^2.0.0" } }, + "querystring": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/querystring/-/querystring-0.2.0.tgz", + "integrity": "sha1-sgmEkgO7Jd+CDadW50cAWHhSFiA=" + }, "range-parser": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", @@ -2043,6 +2597,16 @@ } } }, + "readable-stream": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", + "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", + "requires": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + } + }, "rechoir": { "version": "0.6.2", "resolved": "https://registry.npmjs.org/rechoir/-/rechoir-0.6.2.tgz", @@ -2182,6 +2746,11 @@ "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" }, + "sax": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.1.tgz", + "integrity": "sha1-e45lYZCyKOgaZq6nSEgNgozS03o=" + }, "semver": { "version": "5.7.1", "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", @@ -2271,6 +2840,26 @@ "integrity": "sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0=", "dev": true }, + "simple-swizzle": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/simple-swizzle/-/simple-swizzle-0.2.2.tgz", + "integrity": "sha1-pNprY1/8zMoz9w0Xy5JZLeleVXo=", + "requires": { + "is-arrayish": "^0.3.1" + }, + "dependencies": { + "is-arrayish": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.3.2.tgz", + "integrity": "sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ==" + } + } + }, + "smart-buffer": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/smart-buffer/-/smart-buffer-4.1.0.tgz", + "integrity": "sha512-iVICrxOzCynf/SNaBQCw34eM9jROU/s5rzIhpOvzhzuYHfJR/DhZfDkXiZSgKXfgv26HT3Yni3AV/DGw0cGnnw==" + }, "snapdragon": { "version": "0.8.2", "resolved": "https://registry.npmjs.org/snapdragon/-/snapdragon-0.8.2.tgz", @@ -2384,6 +2973,34 @@ } } }, + "socks": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/socks/-/socks-2.3.3.tgz", + "integrity": "sha512-o5t52PCNtVdiOvzMry7wU4aOqYWL0PeCXRWBEiJow4/i/wr+wpsJQ9awEu1EonLIqsfGd5qSgDdxEOvCdmBEpA==", + "requires": { + "ip": "1.1.5", + "smart-buffer": "^4.1.0" + } + }, + "socks-proxy-agent": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/socks-proxy-agent/-/socks-proxy-agent-4.0.2.tgz", + "integrity": "sha512-NT6syHhI9LmuEMSK6Kd2V7gNv5KFZoLE7V5udWmn0de+3Mkj3UMA/AJPLyeNUVmElCurSHtUdM3ETpR3z770Wg==", + "requires": { + "agent-base": "~4.2.1", + "socks": "~2.3.2" + }, + "dependencies": { + "agent-base": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-4.2.1.tgz", + "integrity": "sha512-JVwXMr9nHYTUXsBFKUqhJwvlcYU/blreOEUkhNR2eXZIvwd+c+o5V4MgDPKWnMS/56awN3TRzIP+KoPn+roQtg==", + "requires": { + "es6-promisify": "^5.0.0" + } + } + } + }, "source-map": { "version": "0.5.7", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", @@ -2428,6 +3045,11 @@ "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=" }, + "stack-trace": { + "version": "0.0.10", + "resolved": "https://registry.npmjs.org/stack-trace/-/stack-trace-0.0.10.tgz", + "integrity": "sha1-VHxws0fo0ytOEI6hoqFZ5f3eGcA=" + }, "static-extend": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/static-extend/-/static-extend-0.1.2.tgz", @@ -2480,6 +3102,21 @@ } } }, + "string_decoder": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "requires": { + "safe-buffer": "~5.2.0" + }, + "dependencies": { + "safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==" + } + } + }, "strip-ansi": { "version": "5.2.0", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", @@ -2518,12 +3155,22 @@ "has-flag": "^3.0.0" } }, + "text-hex": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/text-hex/-/text-hex-1.0.0.tgz", + "integrity": "sha512-uuVGNWzgJ4yhRaNSiubPY7OjISw4sw4E5Uv0wbjp+OzcbmVU/rsT8ujgcXJhn9ypzsgr5vlzpPqP+MBBKcGvbg==" + }, "through": { "version": "2.3.8", "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", "integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=", "dev": true }, + "thunkify": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/thunkify/-/thunkify-2.1.2.tgz", + "integrity": "sha1-+qDp0jDFGsyVyhOjYawFyn4EVT0=" + }, "tmp": { "version": "0.0.33", "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz", @@ -2591,12 +3238,25 @@ "resolved": "https://registry.npmjs.org/toml/-/toml-3.0.0.tgz", "integrity": "sha512-y/mWCZinnvxjTKYhJ+pYxwD0mRLVvOtdS2Awbgxln6iEnt4rk0yBxeSBHkGJcPucRiG0e55mwWp+g/05rsrd6w==" }, + "triple-beam": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/triple-beam/-/triple-beam-1.3.0.tgz", + "integrity": "sha512-XrHUvV5HpdLmIj4uVMxHggLbFSZYIn7HEWsqePZcI50pco+MPqJ50wMGY794X7AOOhxOBAjbkqfAbEe/QMp2Lw==" + }, "tslib": { "version": "1.10.0", "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.10.0.tgz", "integrity": "sha512-qOebF53frne81cf0S9B41ByenJ3/IuH8yJKngAX35CmiZySA0khhkovshKK+jGCaMnVomla7gVlIcc3EvKPbTQ==", "dev": true }, + "type-check": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.3.2.tgz", + "integrity": "sha1-WITKtRLPHTVeP7eE8wgEsrUg23I=", + "requires": { + "prelude-ls": "~1.1.2" + } + }, "type-fest": { "version": "0.13.1", "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.13.1.tgz", @@ -2680,12 +3340,26 @@ "integrity": "sha1-2pN/emLiH+wf0Y1Js1wpNQZ6bHI=", "dev": true }, + "url": { + "version": "0.10.3", + "resolved": "https://registry.npmjs.org/url/-/url-0.10.3.tgz", + "integrity": "sha1-Ah5NnHcF8hu/N9A861h2dAJ3TGQ=", + "requires": { + "punycode": "1.3.2", + "querystring": "0.2.0" + } + }, "use": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/use/-/use-3.1.1.tgz", "integrity": "sha512-cwESVXlO3url9YWlFW/TA9cshCEhtu7IKJ/p5soJ/gGpj7vbvFrAY/eIioQ6Dw23KjZhYgiIo8HOs1nQ2vr/oQ==", "dev": true }, + "util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=" + }, "utils-merge": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", @@ -2710,17 +3384,150 @@ "isexe": "^2.0.0" } }, + "winston": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/winston/-/winston-3.3.3.tgz", + "integrity": "sha512-oEXTISQnC8VlSAKf1KYSSd7J6IWuRPQqDdo8eoRNaYKLvwSb5+79Z3Yi1lrl6KDpU6/VWaxpakDAtb1oQ4n9aw==", + "requires": { + "@dabh/diagnostics": "^2.0.2", + "async": "^3.1.0", + "is-stream": "^2.0.0", + "logform": "^2.2.0", + "one-time": "^1.0.0", + "readable-stream": "^3.4.0", + "stack-trace": "0.0.x", + "triple-beam": "^1.3.0", + "winston-transport": "^4.4.0" + } + }, + "winston-cloudwatch": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/winston-cloudwatch/-/winston-cloudwatch-2.3.2.tgz", + "integrity": "sha512-dUQF0ENHcNVyiEJNTowvfggDbjF7mUw7s3gWVto6pW9pxdKwr682UOXK0wlr4jwNHChD6+n288jwgShn8Spwqw==", + "requires": { + "async": "^3.1.0", + "aws-sdk": "^2.553.0", + "chalk": "^4.0.0", + "fast-safe-stringify": "^2.0.7", + "lodash.assign": "^4.2.0", + "lodash.find": "^4.6.0", + "lodash.isempty": "^4.4.0", + "lodash.iserror": "^3.1.1", + "proxy-agent": "^3.1.1" + }, + "dependencies": { + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "requires": { + "color-convert": "^2.0.1" + } + }, + "chalk": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", + "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==", + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==" + }, + "supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "requires": { + "has-flag": "^4.0.0" + } + } + } + }, + "winston-transport": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/winston-transport/-/winston-transport-4.4.0.tgz", + "integrity": "sha512-Lc7/p3GtqtqPBYYtS6KCN3c77/2QCev51DvcJKbkFPQNoj1sinkGwLGFDxkXY9J6p9+EPnYs+D90uwbnaiURTw==", + "requires": { + "readable-stream": "^2.3.7", + "triple-beam": "^1.2.0" + }, + "dependencies": { + "readable-stream": { + "version": "2.3.7", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", + "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "requires": { + "safe-buffer": "~5.1.0" + } + } + } + }, "word-wrap": { "version": "1.2.3", "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz", - "integrity": "sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==", - "dev": true + "integrity": "sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==" }, "wrappy": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", "dev": true + }, + "xml2js": { + "version": "0.4.19", + "resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.4.19.tgz", + "integrity": "sha512-esZnJZJOiJR9wWKMyuvSE1y6Dq5LCuJanqhxslH2bxM6duahNZ+HMpCLhBQGZkbX6xRf8x1Y2eJlgt2q3qo49Q==", + "requires": { + "sax": ">=0.6.0", + "xmlbuilder": "~9.0.1" + } + }, + "xmlbuilder": { + "version": "9.0.7", + "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-9.0.7.tgz", + "integrity": "sha1-Ey7mPS7FVlxVfiD0wi35rKaGsQ0=" + }, + "xregexp": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/xregexp/-/xregexp-2.0.0.tgz", + "integrity": "sha1-UqY+VsoLhKfzpfPWGHLxJq16WUM=" + }, + "yallist": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", + "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==" } } } diff --git a/package.json b/package.json index 1d4b630e1..25be48f12 100644 --- a/package.json +++ b/package.json @@ -8,6 +8,7 @@ "test": "node -v" }, "dependencies": { + "aws-sdk": "^2.787.0", "axios": "^0.19.0", "base-64": "^0.1.0", "bluebird": "^3.7.0", @@ -22,11 +23,14 @@ "js-yaml": "^3.13.1", "jsonwebtoken": "^8.5.1", "lodash": "^4.17.19", + "moment-timezone": "^0.5.31", "morgan": "~1.9.1", "query-string": "^6.8.3", "serialize-error": "^7.0.1", "toml": "^3.0.0", - "uuid": "^3.3.3" + "uuid": "^3.3.3", + "winston": "^3.3.3", + "winston-cloudwatch": "^2.3.2" }, "devDependencies": { "cz-conventional-changelog": "^3.0.2" From 1ec3d0ba6f91f975e75e30ad49d87ced64133621 Mon Sep 17 00:00:00 2001 From: Jie Hao Kwa Date: Mon, 9 Nov 2020 14:09:59 +0800 Subject: [PATCH 02/10] feat: add singleton CloudWatchLogger class This commit adds a CloudWatchLogger class which uses the singleton pattern. Since a logger does not hold any state, it makes sense to use the singleton pattern. This logger attempts to connect to and log directly to cloudwatch if in a production environment (NODE_ENV = prod or staging) and logs directly to console if in dev mode. In other words, in a prod environment we log both directly to cloudwatch and to console, so we have two sets of logs as a fallback mechanism. In particular, our cloudwatch only logs provide a clean application log which make it easier to debug our own operations. --- logger/logger.js | 101 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 101 insertions(+) create mode 100644 logger/logger.js diff --git a/logger/logger.js b/logger/logger.js new file mode 100644 index 000000000..850a3487a --- /dev/null +++ b/logger/logger.js @@ -0,0 +1,101 @@ +// Imports +const Bluebird = require('bluebird') +const moment = require('moment-timezone') + +// Env vars +const { NODE_ENV } = process.env + +// AWS +const AWS = require('aws-sdk') + +const AWS_REGION_NAME = 'ap-southeast-1' +AWS.config.update({ region: AWS_REGION_NAME }) +const awsMetadata = new AWS.MetadataService() +const metadataRequest = Bluebird.promisify(awsMetadata.request.bind(awsMetadata)) + +// Logging tools +const winston = require('winston') +const WinstonCloudWatch = require('winston-cloudwatch') + +// Constants +const LOG_GROUP_NAME = `${process.env.AWS_BACKEND_EB_ENV_NAME}/nodejs.log` +const IS_PROD_ENV = NODE_ENV !== 'LOCAL_DEV' && NODE_ENV !== 'DEV' + +// Retrieve EC2 instance since that is the cloudwatch log stream name +async function getEc2InstanceId () { + let id + try { + id = await metadataRequest('/latest/meta-data/instance-id').timeout(1000) + } catch (error) { + // eslint-disable-next-line no-console + console.log(timestampGenerator(), 'Error getting ec2 instance id. This script is probably not running on ec2') + throw error + } + return id +} + +function timestampGenerator () { + return moment().tz('Asia/Singapore') + .format('YYYY-MM-DD HH:mm:ss') +} +class CloudWatchLogger { + constructor () { + this._logger = winston.createLogger() + } + + async init () { + if (IS_PROD_ENV) { + try { + // attempt to log directly to cloudwatch + const logGroupName = LOG_GROUP_NAME + const logStreamName = await getEc2InstanceId() + const awsRegion = AWS_REGION_NAME + + const cloudwatchConfig = { + logGroupName, + logStreamName, + awsRegion, + stderrLevels: ['error'], + format: winston.format.simple(), + handleExceptions: true, + } + + this._logger.add(new WinstonCloudWatch(cloudwatchConfig)) + } catch (err) { + console.error(`${timestampGenerator()} ${err.message}`) + console.error(`${timestampGenerator()} Failed to initiate CloudWatch logger`) + } + } + } + + // this method is used to log non-error messages, replacing console.log + async info (logMessage) { + // eslint-disable-next-line no-console + console.log(`${timestampGenerator()} ${logMessage}`) + + if (IS_PROD_ENV) { + try { + await this._logger.info(`${timestampGenerator()} ${logMessage}`) + } catch (err) { + console.error(`${timestampGenerator()} ${err.message}`) + } + } + } + + // this method is used to log error messages and write to stderr, replacing console.error + async error (errMessage) { + console.error(`${timestampGenerator()} ${errMessage}`) + + if (IS_PROD_ENV) { + try { + await this._logger.error(`${timestampGenerator()} ${errMessage}`) + } catch (err) { + console.error(`${timestampGenerator()} ${err.message}`) + } + } + } +} + +const logger = new CloudWatchLogger() + +module.exports = logger From 1af0ad7d929096ee4b3a65ab76b3dcf19a5dce78 Mon Sep 17 00:00:00 2001 From: Jie Hao Kwa Date: Mon, 9 Nov 2020 14:16:30 +0800 Subject: [PATCH 03/10] chore: remove try-catch statements This commit removes try-catch statements that we were supposed to remove in PR #63 but which we forgot to do so. These blocks are no longer needed since we have a central error handler which deals with errors. --- classes/Config.js | 31 ++++++----- classes/Settings.js | 122 +++++++++++++++++++++----------------------- routes/documents.js | 32 +++++------- routes/homepage.js | 24 ++++----- routes/resources.js | 27 ++++------ 5 files changed, 107 insertions(+), 129 deletions(-) diff --git a/classes/Config.js b/classes/Config.js index 1ac8bf603..066541fe6 100644 --- a/classes/Config.js +++ b/classes/Config.js @@ -1,6 +1,9 @@ const axios = require('axios'); const validateStatus = require('../utils/axios-utils') +// Import logger +const logger = require('../logger/logger'); + // Import error const { NotFoundError } = require('../errors/NotFoundError') @@ -42,25 +45,21 @@ class Config { } async update(newContent, sha) { - try { - const endpoint = `https://api.github.com/repos/${GITHUB_ORG_NAME}/${this.siteName}/contents/_config.yml` + const endpoint = `https://api.github.com/repos/${GITHUB_ORG_NAME}/${this.siteName}/contents/_config.yml` - const params = { - "message": 'Edit config', - "content": newContent, - "branch": BRANCH_REF, - "sha": sha - } + const params = { + "message": 'Edit config', + "content": newContent, + "branch": BRANCH_REF, + "sha": sha + } await axios.put(endpoint, params, { - headers: { - Authorization: `token ${this.accessToken}`, - "Content-Type": "application/json" - } - }) - } catch (err) { - console.log(err) - } + headers: { + Authorization: `token ${this.accessToken}`, + "Content-Type": "application/json" + } + }) } } diff --git a/classes/Settings.js b/classes/Settings.js index d109ac922..16dcea754 100644 --- a/classes/Settings.js +++ b/classes/Settings.js @@ -16,75 +16,67 @@ class Settings { } async get() { - try { - // retrieve _config.yml and footer.yml - const configResp = new Config(this.accessToken, this.siteName) - - const IsomerDataFile = new File(this.accessToken, this.siteName) - const dataType = new DataType() - IsomerDataFile.setFileType(dataType) - - const fileRetrievalArr = [configResp.read(), IsomerDataFile.read(FOOTER_PATH)] - - const fileContentsArr = await Bluebird.map(fileRetrievalArr, async (fileOp) => { - const { content, sha } = await fileOp - return { content, sha} - }) - - // convert data to object form - const configContent = fileContentsArr[0].content - const footerContent = fileContentsArr[1].content - - const configReadableContent = yaml.safeLoad(Base64.decode(configContent)); - const footerReadableContent = yaml.safeLoad(Base64.decode(footerContent)); - - // retrieve only the relevant config and index fields - const configFieldsRequired = { - url: configReadableContent.url, - title: configReadableContent.title, - favicon: configReadableContent.favicon, - resources_name: configReadableContent.resources_name, - colors: configReadableContent.colors, - } - - // retrieve footer sha since we are sending the footer object wholesale - const footerSha = fileContentsArr[1].sha - - return ({ configFieldsRequired, footerContent: footerReadableContent, footerSha }) - } catch (err) { - console.log(err) + // retrieve _config.yml and footer.yml + const configResp = new Config(this.accessToken, this.siteName) + + const IsomerDataFile = new File(this.accessToken, this.siteName) + const dataType = new DataType() + IsomerDataFile.setFileType(dataType) + + const fileRetrievalArr = [configResp.read(), IsomerDataFile.read(FOOTER_PATH)] + + const fileContentsArr = await Bluebird.map(fileRetrievalArr, async (fileOp) => { + const { content, sha } = await fileOp + return { content, sha} + }) + + // convert data to object form + const configContent = fileContentsArr[0].content + const footerContent = fileContentsArr[1].content + + const configReadableContent = yaml.safeLoad(Base64.decode(configContent)); + const footerReadableContent = yaml.safeLoad(Base64.decode(footerContent)); + + // retrieve only the relevant config and index fields + const configFieldsRequired = { + url: configReadableContent.url, + title: configReadableContent.title, + favicon: configReadableContent.favicon, + resources_name: configReadableContent.resources_name, + colors: configReadableContent.colors, } + + // retrieve footer sha since we are sending the footer object wholesale + const footerSha = fileContentsArr[1].sha + + return ({ configFieldsRequired, footerContent: footerReadableContent, footerSha }) } async post(payload) { - try { - // setup - const configResp = new Config(this.accessToken, this.siteName) - const config = await configResp.read() - const IsomerDataFile = new File(this.accessToken, this.siteName) - const dataType = new DataType() - IsomerDataFile.setFileType(dataType) - - // extract data - const { - footerSettings, - configSettings, - footerSha, - } = payload - - // update config object - const configContent = yaml.safeLoad(Base64.decode(config.content)); - Object.keys(configSettings).forEach((setting) => (configContent[setting] = configSettings[setting])); - - // update files - const newConfigContent = Base64.encode(yaml.safeDump(configContent)) - const newFooterContent = Base64.encode(yaml.safeDump(footerSettings)) - await configResp.update(newConfigContent, config.sha) - await IsomerDataFile.update(FOOTER_PATH, newFooterContent, footerSha) - return - } catch (err) { - console.log(err) - } + // setup + const configResp = new Config(this.accessToken, this.siteName) + const config = await configResp.read() + const IsomerDataFile = new File(this.accessToken, this.siteName) + const dataType = new DataType() + IsomerDataFile.setFileType(dataType) + + // extract data + const { + footerSettings, + configSettings, + footerSha, + } = payload + + // update config object + const configContent = yaml.safeLoad(Base64.decode(config.content)); + Object.keys(configSettings).forEach((setting) => (configContent[setting] = configSettings[setting])); + + // update files + const newConfigContent = Base64.encode(yaml.safeDump(configContent)) + const newFooterContent = Base64.encode(yaml.safeDump(footerSettings)) + await configResp.update(newConfigContent, config.sha) + await IsomerDataFile.update(FOOTER_PATH, newFooterContent, footerSha) + return } } diff --git a/routes/documents.js b/routes/documents.js index e112cef15..9ec1119ae 100644 --- a/routes/documents.js +++ b/routes/documents.js @@ -54,24 +54,20 @@ async function readDocument (req, res, next) { // Update document async function updateDocument (req, res, next) { - try { - const { accessToken } = req - - const { siteName, documentName } = req.params - const { content, sha } = req.body - - // TO-DO: - // Validate pageName and content - - const IsomerFile = new File(accessToken, siteName) - const documentType = new DocumentType() - IsomerFile.setFileType(documentType) - const { newSha } = await IsomerFile.update(documentName, content, sha) - - res.status(200).json({ documentName, content, sha: newSha }) - } catch (err) { - console.log(err) - } + const { accessToken } = req + + const { siteName, documentName } = req.params + const { content, sha } = req.body + + // TO-DO: + // Validate pageName and content + + const IsomerFile = new File(accessToken, siteName) + const documentType = new DocumentType() + IsomerFile.setFileType(documentType) + const { newSha } = await IsomerFile.update(documentName, content, sha) + + res.status(200).json({ documentName, content, sha: newSha }) } // Delete document diff --git a/routes/homepage.js b/routes/homepage.js index de0f8827d..55d074461 100644 --- a/routes/homepage.js +++ b/routes/homepage.js @@ -29,24 +29,20 @@ async function readHomepage (req, res, next) { // Update homepage index file async function updateHomepage (req, res, next) { - try { - const { accessToken } = req + const { accessToken } = req - const { siteName } = req.params - const { content, sha } = req.body + const { siteName } = req.params + const { content, sha } = req.body - // TO-DO: - // Validate content + // TO-DO: + // Validate content - const IsomerFile = new File(accessToken, siteName) - const homepageType = new HomepageType() - IsomerFile.setFileType(homepageType) - const { newSha } = await IsomerFile.update(HOMEPAGE_INDEX_PATH, content, sha) + const IsomerFile = new File(accessToken, siteName) + const homepageType = new HomepageType() + IsomerFile.setFileType(homepageType) + const { newSha } = await IsomerFile.update(HOMEPAGE_INDEX_PATH, content, sha) - res.status(200).json({ content, sha: newSha }) - } catch (err) { - console.log(err) - } + res.status(200).json({ content, sha: newSha }) } router.get('/:siteName/homepage', attachRouteHandlerWrapper(readHomepage)) diff --git a/routes/resources.js b/routes/resources.js index 8cbbe89fc..d17d74284 100644 --- a/routes/resources.js +++ b/routes/resources.js @@ -24,22 +24,17 @@ async function listResources (req, res, next) { // Create new resource async function createNewResource (req, res, next) { - try { - const { accessToken } = req - const { siteName } = req.params - const { resourceName } = req.body - - const IsomerResourceRoom = new ResourceRoom(accessToken, siteName) - const resourceRoomName = await IsomerResourceRoom.get() - - const IsomerResource = new Resource(accessToken, siteName) - await IsomerResource.create(resourceRoomName, resourceName) - - res.status(200).json({ resourceName }) - // TO-DO - } catch (err) { - console.log(err) - } + const { accessToken } = req + const { siteName } = req.params + const { resourceName } = req.body + + const IsomerResourceRoom = new ResourceRoom(accessToken, siteName) + const resourceRoomName = await IsomerResourceRoom.get() + + const IsomerResource = new Resource(accessToken, siteName) + await IsomerResource.create(resourceRoomName, resourceName) + + res.status(200).json({ resourceName }) } // Delete resource From 2d00ab4630fe134a44bc4ffdeac274ebda1c41ab Mon Sep 17 00:00:00 2001 From: Jie Hao Kwa Date: Mon, 9 Nov 2020 14:18:10 +0800 Subject: [PATCH 04/10] feat: replace existing usage of console.log This commit replaces existing usage of console.log with our new logger class' `logger.info` method --- bin/www | 9 ++++++--- middleware/errorHandler.js | 6 +++++- 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/bin/www b/bin/www index bc52d203d..a326c38c2 100755 --- a/bin/www +++ b/bin/www @@ -8,6 +8,9 @@ const app = require('../server'); const debug = require('debug')('isomercms:server'); const http = require('http'); +// Import logger +const logger = require('../logger/logger'); + /** * Get port from environment and store in Express. */ @@ -65,11 +68,11 @@ function onError(error) { // handle specific listen errors with friendly messages switch (error.code) { case 'EACCES': - console.error(bind + ' requires elevated privileges'); + logger.error(bind + ' requires elevated privileges'); process.exit(1); break; case 'EADDRINUSE': - console.error(bind + ' is already in use'); + logger.error(bind + ' is already in use'); process.exit(1); break; default: @@ -87,5 +90,5 @@ function onListening() { ? 'pipe ' + addr : 'port ' + addr.port; debug('Listening on ' + bind); - console.log(`isomerCMS app listening on port ${port}`) + logger.info(`isomerCMS app listening on port ${port}`) } diff --git a/middleware/errorHandler.js b/middleware/errorHandler.js index d5615c2c1..78dfbc956 100644 --- a/middleware/errorHandler.js +++ b/middleware/errorHandler.js @@ -1,7 +1,11 @@ +// Import dependencies const { serializeError } = require('serialize-error') +// Import logger +const logger = require('../logger/logger'); + function errorHandler (err, req, res, next) { - console.log(`${new Date()}: ${JSON.stringify(serializeError(err))}`) + logger.info(`${new Date()}: ${JSON.stringify(serializeError(err))}`) // set locals, only providing error in development res.locals.message = err.message; From 10637c782a04c9735cefff4c20c0198618658e86 Mon Sep 17 00:00:00 2001 From: Jie Hao Kwa Date: Mon, 9 Nov 2020 14:41:22 +0800 Subject: [PATCH 05/10] feat: retrieve user id for logging purposes This commit retrieves a user's GitHub id during the login authentication stage for auditing purposes so that we can log user actions when they perform actions on the CMS. --- routes/auth.js | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/routes/auth.js b/routes/auth.js index 1dfb65646..7b9d1c265 100644 --- a/routes/auth.js +++ b/routes/auth.js @@ -1,6 +1,7 @@ const express = require('express'); const router = express.Router(); const axios = require('axios'); +const validateStatus = require('../utils/axios-utils') const queryString = require('query-string'); // Import error @@ -34,6 +35,21 @@ async function githubAuth (req, res, next) { const access_token = queryString.parse(resp.data).access_token if (!access_token) throw new AuthError ('Access token not found') + // Retrieve user information to put into access token + const endpoint = `https://api.github.com/user` + const userResp = await axios.get(endpoint, { + validateStatus, + params, + headers: { + Authorization: `token ${access_token}`, + Accept: 'application/vnd.github.v3+json', + "Content-Type": "application/json" + } + }) + + let userId + if (userResp.data) userId = userResp.data.login + const authTokenExpiry = new Date() authTokenExpiry.setTime(authTokenExpiry.getTime() + AUTH_TOKEN_EXPIRY_MS) @@ -45,7 +61,7 @@ async function githubAuth (req, res, next) { secure: process.env.NODE_ENV !== 'DEV' && process.env.NODE_ENV !== 'LOCAL_DEV', } - const token = jwtUtils.signToken({access_token}) + const token = jwtUtils.signToken({access_token, user_id: userId}) res.cookie(COOKIE_NAME, token, cookieSettings) From 149d062a1cc6c2c9bc0cf65c994985bb0bacb661 Mon Sep 17 00:00:00 2001 From: Jie Hao Kwa Date: Mon, 9 Nov 2020 14:46:54 +0800 Subject: [PATCH 06/10] feat: add userId attribute to request object in auth middleware This commit extracts the newly added user_id attribute from the JWT during the request authentication stage and attaches it to the request object. This allows us to identify user logs by their GitHub user id instead of an unidentifiable access token string. --- middleware/auth.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/middleware/auth.js b/middleware/auth.js index 68ca6df7e..e7c922294 100644 --- a/middleware/auth.js +++ b/middleware/auth.js @@ -17,8 +17,9 @@ function noVerify (req, res, next) { const verifyJwt = (req, res, next) => { try { const { isomercms } = req.cookies - const { access_token } = jwtUtils.verifyToken(isomercms) + const { access_token, user_id } = jwtUtils.verifyToken(isomercms) req.accessToken = access_token + req.userId = user_id } catch (err) { console.error('Authentication error') if (err.name === 'TokenExpiredError') { From cef291d5db3451e7921ac568954b3589c9a2f40f Mon Sep 17 00:00:00 2001 From: Jie Hao Kwa Date: Mon, 9 Nov 2020 15:04:08 +0800 Subject: [PATCH 07/10] fix: assign verifyJwt auth function to logout endpoint Previously, we erroneously assigned the logout endpoint the `noVerify` auth function. However, the logout endpoint should not be accesible to users who have not logged in, therefore it should be a protected endpoint. --- middleware/auth.js | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/middleware/auth.js b/middleware/auth.js index e7c922294..ac03b52d7 100644 --- a/middleware/auth.js +++ b/middleware/auth.js @@ -2,6 +2,9 @@ const express = require('express') const jwtUtils = require('../utils/jwt-utils') +// Import logger +const logger = require('../logger/logger') + // Import errors const { AuthError } = require('../errors/AuthError') const { verify } = require('jsonwebtoken') @@ -21,7 +24,7 @@ const verifyJwt = (req, res, next) => { req.accessToken = access_token req.userId = user_id } catch (err) { - console.error('Authentication error') + logger.error('Authentication error') if (err.name === 'TokenExpiredError') { throw new AuthError('JWT token has expired') } @@ -35,7 +38,7 @@ const verifyJwt = (req, res, next) => { // Login and logout auth.get('/auth', noVerify) -auth.get('/auth/logout', noVerify) +auth.get('/auth/logout', verifyJwt) // Index auth.get('/', noVerify) From 67069a9526da3e0a2ce5b609286f06118ad142f2 Mon Sep 17 00:00:00 2001 From: Jie Hao Kwa Date: Mon, 9 Nov 2020 15:06:30 +0800 Subject: [PATCH 08/10] feat: add api-logging middleware This commit adds an api logger middleware which logs all api requests that have been submitted to the server for auditing purposes. Note that this middleware is added AFTER the auth middleware instead of before - that's because we're reliant on the auth middleware to add the user id to the request object which will allow our api logs to be identifiable. --- middleware/apiLogger.js | 33 +++++++++++++++++++++++++++++++++ server.js | 4 ++++ 2 files changed, 37 insertions(+) create mode 100644 middleware/apiLogger.js diff --git a/middleware/apiLogger.js b/middleware/apiLogger.js new file mode 100644 index 000000000..e872343c7 --- /dev/null +++ b/middleware/apiLogger.js @@ -0,0 +1,33 @@ +// Imports +const express = require('express') + +// Logger +const logger = require('../logger/Logger') + +const apiLogger = express.Router() + +apiLogger.use((req, res, next) => { + function isObjEmpty (obj) { + return Object.keys(obj).length === 0 + } + + // Get IP address + const ipAddress = req.headers['x-forwarded-for'] + console.log(req.headers) + + // Get user GitHub id + let userId + if (req.userId) userId = req.userId + + let logMessage = `User ${userId} from IP address ${ipAddress ? `(IP: ${ipAddress})` : undefined } called ${req.method} on ${req.path}` + if (!isObjEmpty(req.body)) { + logMessage += ` with body ${JSON.stringify(req.body)}` + } + if (!isObjEmpty(req.query)) { + logMessage += ` with query ${JSON.stringify(req.query)}` + } + logger.info(logMessage) + return next() +}) + +module.exports = { apiLogger } diff --git a/server.js b/server.js index 860e2c161..219a99e62 100644 --- a/server.js +++ b/server.js @@ -9,6 +9,7 @@ const cors = require('cors'); const FRONTEND_URL = process.env.FRONTEND_URL // Import middleware +const { apiLogger } = require('./middleware/apiLogger') const { auth } = require('./middleware/auth') const { errorHandler } = require('./middleware/errorHandler') @@ -46,6 +47,9 @@ app.use(cors({ // Use auth middleware app.use(auth) +// Log api requests +app.use(apiLogger) + // Routes layer setup app.use('/', indexRouter); app.use('/auth', authRouter); From 71f2ffcdf30d1911588d3dedb6bdd55a7e2af538 Mon Sep 17 00:00:00 2001 From: Jie Hao Kwa Date: Fri, 20 Nov 2020 10:04:15 +0800 Subject: [PATCH 09/10] fix: remove logging of request body This commit modifies the API logger so that we log the request body. The request body usually contains encrypted post content, and the relevant file sha, which isn't important to us when we're sifting the logs. Furthermore, we can identify the files being changes simply by looking at the request path and date-time of the request - from these, we would be able to identify the file being changed and also identify the file version from the date-time of teh request. that useful to us when we --- middleware/apiLogger.js | 4 ---- 1 file changed, 4 deletions(-) diff --git a/middleware/apiLogger.js b/middleware/apiLogger.js index e872343c7..5fac8d2f5 100644 --- a/middleware/apiLogger.js +++ b/middleware/apiLogger.js @@ -13,16 +13,12 @@ apiLogger.use((req, res, next) => { // Get IP address const ipAddress = req.headers['x-forwarded-for'] - console.log(req.headers) // Get user GitHub id let userId if (req.userId) userId = req.userId let logMessage = `User ${userId} from IP address ${ipAddress ? `(IP: ${ipAddress})` : undefined } called ${req.method} on ${req.path}` - if (!isObjEmpty(req.body)) { - logMessage += ` with body ${JSON.stringify(req.body)}` - } if (!isObjEmpty(req.query)) { logMessage += ` with query ${JSON.stringify(req.query)}` } From 02cbdde0c1a465b5c0dbcc38aa0be193096fc4e5 Mon Sep 17 00:00:00 2001 From: Jie Hao Kwa Date: Fri, 20 Nov 2020 10:09:53 +0800 Subject: [PATCH 10/10] fix: unprotect logout endpoint Currently, the logout endpoint is protected by the `verifyJwt` auth function. However, this prevents auto-logout functionality for when a user's token expires, leaving them stuck in the UI with a useless JWT which is incapable of making successful API calls. This commit reverts the endpoint to be unprotected to fix this problem. --- middleware/auth.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/middleware/auth.js b/middleware/auth.js index ac03b52d7..88855bfe3 100644 --- a/middleware/auth.js +++ b/middleware/auth.js @@ -38,7 +38,7 @@ const verifyJwt = (req, res, next) => { // Login and logout auth.get('/auth', noVerify) -auth.get('/auth/logout', verifyJwt) +auth.get('/auth/logout', noVerify) // Index auth.get('/', noVerify)