diff --git a/package-lock.json b/package-lock.json index 0a9ded1..a5896aa 100644 --- a/package-lock.json +++ b/package-lock.json @@ -2619,6 +2619,15 @@ "resolved": "https://registry.npmjs.org/@types/minimatch/-/minimatch-3.0.3.tgz", "integrity": "sha512-tHq6qdbT9U1IRSGf14CL0pUlULksvY9OZ+5eEgl1N7t+OA3tGvNpxJCzuKQlsNgCVwbAs670L1vcVQi8j9HjnA==" }, + "@types/moment-timezone": { + "version": "0.5.12", + "resolved": "https://registry.npmjs.org/@types/moment-timezone/-/moment-timezone-0.5.12.tgz", + "integrity": "sha512-hnHH2+Efg2vExr/dSz+IX860nSiyk9Sk4pJF2EmS11lRpMcNXeB4KBW5xcgw2QPsb9amTXdsVNEe5IoJXiT0uw==", + "dev": true, + "requires": { + "moment": ">=2.14.0" + } + }, "@types/next-redux-saga": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/@types/next-redux-saga/-/next-redux-saga-3.0.1.tgz", @@ -2772,6 +2781,14 @@ "@types/node": "*" } }, + "@types/winston": { + "version": "2.4.4", + "resolved": "https://registry.npmjs.org/@types/winston/-/winston-2.4.4.tgz", + "integrity": "sha512-BVGCztsypW8EYwJ+Hq+QNYiT/MUyCif0ouBH+flrY66O5W+KIXAMML6E/0fJpm7VjIzgangahl5S03bJJQGrZw==", + "requires": { + "winston": "*" + } + }, "@types/yargs": { "version": "12.0.12", "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-12.0.12.tgz", @@ -3336,6 +3353,14 @@ "integrity": "sha512-+Ryf6g3BKoRc7jfp7ad8tM4TtMiaWvbF/1/sQcZPkkS7ag3D5nMBCe2UfOTONtAkaG0tO0ij3C5Lwmf1EiyjHg==", "dev": true }, + "async": { + "version": "2.6.3", + "resolved": "https://registry.npmjs.org/async/-/async-2.6.3.tgz", + "integrity": "sha512-zflvls11DCy+dQWzTW2dzuilv8Z5X/pjfmZOWba6TNIVDm+2UDaJmXSOXlasHKfNBs8oo3M0aT50fDEWfKZjXg==", + "requires": { + "lodash": "^4.17.14" + } + }, "async-each": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/async-each/-/async-each-1.0.3.tgz", @@ -4375,6 +4400,15 @@ "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", @@ -4388,11 +4422,34 @@ "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=" }, + "color-string": { + "version": "1.5.3", + "resolved": "https://registry.npmjs.org/color-string/-/color-string-1.5.3.tgz", + "integrity": "sha512-dC2C5qeWoYkxki5UAXapdjqO672AM4vZuPGRQfO8b5HKuKGBbKWpITyDYN7TOFKvRW7kOgAn3746clDBMDJyQw==", + "requires": { + "color-name": "^1.0.0", + "simple-swizzle": "^0.2.2" + } + }, + "colornames": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/colornames/-/colornames-1.1.1.tgz", + "integrity": "sha1-+IiQMGhcfE/54qVZ9Qd+t2qBb5Y=" + }, "colors": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/colors/-/colors-1.1.2.tgz", "integrity": "sha1-FopHAXVran9RoSzgyXv6KMCE7WM=" }, + "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" + } + }, "columnify": { "version": "1.5.4", "resolved": "https://registry.npmjs.org/columnify/-/columnify-1.5.4.tgz", @@ -5255,6 +5312,16 @@ "wrappy": "1" } }, + "diagnostics": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/diagnostics/-/diagnostics-1.1.1.tgz", + "integrity": "sha512-8wn1PmdunLJ9Tqbx+Fx/ZEuHfJf4NKSN2ZBj7SJC/OWRWha843+WsTjqMe1B5E3p28jqBlp+mJ2fPVxPyNgYKQ==", + "requires": { + "colorspace": "1.1.x", + "enabled": "1.0.x", + "kuler": "1.0.x" + } + }, "diff": { "version": "3.5.0", "resolved": "https://registry.npmjs.org/diff/-/diff-3.5.0.tgz", @@ -5420,6 +5487,14 @@ "resolved": "https://registry.npmjs.org/emojis-list/-/emojis-list-2.1.0.tgz", "integrity": "sha1-TapNnbAPmBmIDHn6RXrlsJof04k=" }, + "enabled": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/enabled/-/enabled-1.0.2.tgz", + "integrity": "sha1-ll9lE9LC0cX0ZStkouM5ZGf8L5M=", + "requires": { + "env-variable": "0.0.x" + } + }, "encodeurl": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", @@ -5462,6 +5537,11 @@ "resolved": "https://registry.npmjs.org/env-paths/-/env-paths-1.0.0.tgz", "integrity": "sha1-QWgTO0K7BcOKNbGuQ5fIKYqzaeA=" }, + "env-variable": { + "version": "0.0.5", + "resolved": "https://registry.npmjs.org/env-variable/-/env-variable-0.0.5.tgz", + "integrity": "sha512-zoB603vQReOFvTg5xMl9I1P2PnHsHQQKTEowsKKD7nseUfJq6UWzK+4YtlWUO1nhiQUxe6XMkk+JleSZD1NZFA==" + }, "enzyme": { "version": "3.10.0", "resolved": "https://registry.npmjs.org/enzyme/-/enzyme-3.10.0.tgz", @@ -5989,6 +6069,11 @@ "integrity": "sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=", "dev": true }, + "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==" + }, "fb-watchman": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/fb-watchman/-/fb-watchman-2.0.0.tgz", @@ -5998,6 +6083,11 @@ "bser": "^2.0.0" } }, + "fecha": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fecha/-/fecha-2.3.3.tgz", + "integrity": "sha512-lUGBnIamTAwk4znq5BcqsDaxSmZ9nDVJaij6NvRt/Tg4R69gERA+otPKbS86ROw9nxVMw2/mp1fnaiWqbs6Sdg==" + }, "figgy-pudding": { "version": "3.5.1", "resolved": "https://registry.npmjs.org/figgy-pudding/-/figgy-pudding-3.5.1.tgz", @@ -7393,6 +7483,11 @@ "sshpk": "^1.7.0" } }, + "http-status-codes": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/http-status-codes/-/http-status-codes-1.3.2.tgz", + "integrity": "sha512-nDUtj0ltIt08tGi2VWSpSzNNFye0v3YSe9lX3lIqLTuVvvRiYCvs4QQBSHo0eomFYw1wlUuofurUAlTm+vHnXg==" + }, "https-browserify": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/https-browserify/-/https-browserify-1.0.0.tgz", @@ -7668,6 +7763,206 @@ "resolved": "https://registry.npmjs.org/inversify/-/inversify-5.0.1.tgz", "integrity": "sha512-Ieh06s48WnEYGcqHepdsJUIJUXpwH5o5vodAX+DK2JA/gjy4EbEcQZxw+uFfzysmKjiLXGYwNG3qDZsKVMcINQ==" }, + "inversify-express-utils": { + "version": "6.3.2", + "resolved": "https://registry.npmjs.org/inversify-express-utils/-/inversify-express-utils-6.3.2.tgz", + "integrity": "sha512-zIFMJVPTcXzxZBmwtWV2b26MGwPzbZ/XM5SPnrE0SqNQ3QqgI8LMV3nbCLhaPrSji5KToo77UXK5yFpPdVgWJQ==", + "requires": { + "express": "4.16.2", + "http-status-codes": "^1.3.0" + }, + "dependencies": { + "body-parser": { + "version": "1.18.2", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.18.2.tgz", + "integrity": "sha1-h2eKGdhLR9hZuDGZvVm84iKxBFQ=", + "requires": { + "bytes": "3.0.0", + "content-type": "~1.0.4", + "debug": "2.6.9", + "depd": "~1.1.1", + "http-errors": "~1.6.2", + "iconv-lite": "0.4.19", + "on-finished": "~2.3.0", + "qs": "6.5.1", + "raw-body": "2.3.2", + "type-is": "~1.6.15" + } + }, + "content-disposition": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.2.tgz", + "integrity": "sha1-DPaLud318r55YcOoUXjLhdunjLQ=" + }, + "cookie": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.3.1.tgz", + "integrity": "sha1-5+Ch+e9DtMi6klxcWpboBtFoc7s=" + }, + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "requires": { + "ms": "2.0.0" + } + }, + "express": { + "version": "4.16.2", + "resolved": "https://registry.npmjs.org/express/-/express-4.16.2.tgz", + "integrity": "sha1-41xt/i1kt9ygpc1PIXgb4ymeB2w=", + "requires": { + "accepts": "~1.3.4", + "array-flatten": "1.1.1", + "body-parser": "1.18.2", + "content-disposition": "0.5.2", + "content-type": "~1.0.4", + "cookie": "0.3.1", + "cookie-signature": "1.0.6", + "debug": "2.6.9", + "depd": "~1.1.1", + "encodeurl": "~1.0.1", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "finalhandler": "1.1.0", + "fresh": "0.5.2", + "merge-descriptors": "1.0.1", + "methods": "~1.1.2", + "on-finished": "~2.3.0", + "parseurl": "~1.3.2", + "path-to-regexp": "0.1.7", + "proxy-addr": "~2.0.2", + "qs": "6.5.1", + "range-parser": "~1.2.0", + "safe-buffer": "5.1.1", + "send": "0.16.1", + "serve-static": "1.13.1", + "setprototypeof": "1.1.0", + "statuses": "~1.3.1", + "type-is": "~1.6.15", + "utils-merge": "1.0.1", + "vary": "~1.1.2" + } + }, + "finalhandler": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.0.tgz", + "integrity": "sha1-zgtoVbRYU+eRsvzGgARtiCU91/U=", + "requires": { + "debug": "2.6.9", + "encodeurl": "~1.0.1", + "escape-html": "~1.0.3", + "on-finished": "~2.3.0", + "parseurl": "~1.3.2", + "statuses": "~1.3.1", + "unpipe": "~1.0.0" + } + }, + "http-errors": { + "version": "1.6.3", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.6.3.tgz", + "integrity": "sha1-i1VoC7S+KDoLW/TqLjhYC+HZMg0=", + "requires": { + "depd": "~1.1.2", + "inherits": "2.0.3", + "setprototypeof": "1.1.0", + "statuses": ">= 1.4.0 < 2" + }, + "dependencies": { + "statuses": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz", + "integrity": "sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow=" + } + } + }, + "iconv-lite": { + "version": "0.4.19", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.19.tgz", + "integrity": "sha512-oTZqweIP51xaGPI4uPa56/Pri/480R+mo7SeU+YETByQNhDG55ycFyNLIgta9vXhILrxXDmF7ZGhqZIcuN0gJQ==" + }, + "inherits": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", + "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=" + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" + }, + "path-to-regexp": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", + "integrity": "sha1-32BBeABfUi8V60SQ5yR6G/qmf4w=" + }, + "qs": { + "version": "6.5.1", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.1.tgz", + "integrity": "sha512-eRzhrN1WSINYCDCbrz796z37LOe3m5tmW7RQf6oBntukAG1nmovJvhnwHHRMAfeoItc1m2Hk02WER2aQ/iqs+A==" + }, + "raw-body": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.3.2.tgz", + "integrity": "sha1-vNYMd9Prk83gBQKVw/N5OJvIj4k=", + "requires": { + "bytes": "3.0.0", + "http-errors": "1.6.2", + "iconv-lite": "0.4.19", + "unpipe": "1.0.0" + }, + "dependencies": { + "depd": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.1.tgz", + "integrity": "sha1-V4O04cRZ8G+lyif5kfPQbnoxA1k=" + }, + "http-errors": { + "version": "1.6.2", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.6.2.tgz", + "integrity": "sha1-CgAsyFcHGSp+eUbO7cERVfYOxzY=", + "requires": { + "depd": "1.1.1", + "inherits": "2.0.3", + "setprototypeof": "1.0.3", + "statuses": ">= 1.3.1 < 2" + } + }, + "setprototypeof": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.0.3.tgz", + "integrity": "sha1-ZlZ+NwQ+608E2RvWWMDL77VbjgQ=" + } + } + }, + "safe-buffer": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.1.tgz", + "integrity": "sha512-kKvNJn6Mm93gAczWVJg7wH+wGYWNrDHdWvpUmHyEsgCtIwwo3bqPtV4tR5tuPaUhTOo/kvhVwd8XwwOllGYkbg==" + }, + "serve-static": { + "version": "1.13.1", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.13.1.tgz", + "integrity": "sha512-hSMUZrsPa/I09VYFJwa627JJkNs0NrfL1Uzuup+GqHfToR2KcsXFymXSV90hoyw3M+msjFuQly+YzIH/q0MGlQ==", + "requires": { + "encodeurl": "~1.0.1", + "escape-html": "~1.0.3", + "parseurl": "~1.3.2", + "send": "0.16.1" + } + }, + "setprototypeof": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.0.tgz", + "integrity": "sha512-BvE/TwpZX4FXExxOxZyRGQQv651MSwmWKZGqvmPcRIjDqWub67kTKuIMx43cZZrS/cBBzwBcNDWoFxt2XEFIpQ==" + }, + "statuses": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.3.1.tgz", + "integrity": "sha1-+vUbnrdKrvOzrPStX2Gr8ky3uT4=" + } + } + }, "invert-kv": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/invert-kv/-/invert-kv-2.0.0.tgz", @@ -8732,6 +9027,14 @@ "integrity": "sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==", "dev": true }, + "kuler": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/kuler/-/kuler-1.0.1.tgz", + "integrity": "sha512-J9nVUucG1p/skKul6DU3PUZrhs0LPulNaeUOox0IyXDi8S4CztTHs1gQphhuZmzXG7VOQSf6NJfKuzteQLv9gQ==", + "requires": { + "colornames": "^1.1.1" + } + }, "launch-editor": { "version": "2.2.1", "resolved": "https://registry.npmjs.org/launch-editor/-/launch-editor-2.2.1.tgz", @@ -8927,6 +9230,25 @@ "resolved": "https://registry.npmjs.org/lodash.uniq/-/lodash.uniq-4.5.0.tgz", "integrity": "sha1-0CJTc662Uq3BvILklFM5qEJ1R3M=" }, + "logform": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/logform/-/logform-2.1.2.tgz", + "integrity": "sha512-+lZh4OpERDBLqjiwDLpAWNQu6KMjnlXH2ByZwCuSqVPJletw0kTWJf5CgSNAUKn1KUkv3m2cUz/LK8zyEy7wzQ==", + "requires": { + "colors": "^1.2.1", + "fast-safe-stringify": "^2.0.4", + "fecha": "^2.3.3", + "ms": "^2.1.1", + "triple-beam": "^1.3.0" + }, + "dependencies": { + "colors": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/colors/-/colors-1.4.0.tgz", + "integrity": "sha512-a+UqTh4kgZg/SlGvfbzDHpgRu7AAQOmmqRHJnxhRZICKFUT91brVhNNt58CMWU9PsBbv3PDCZUHbVxuDiH2mtA==" + } + } + }, "loose-envify": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", @@ -9369,6 +9691,19 @@ "resolved": "https://registry.npmjs.org/modify-values/-/modify-values-1.0.1.tgz", "integrity": "sha512-xV2bxeN6F7oYjZWTe/YPAy6MN2M+sL4u/Rlm2AHCIVGfo2p1yGmBHQ6vHehl4bRTZBdHu3TSkWdYgkwpYzAGSw==" }, + "moment": { + "version": "2.24.0", + "resolved": "https://registry.npmjs.org/moment/-/moment-2.24.0.tgz", + "integrity": "sha512-bV7f+6l2QigeBBZSM/6yTNq4P2fNpSWj/0e7jQcy87A8e7o2nAfP/34/2ky5Vw4B9S446EtIhodAzkFCcR4dQg==" + }, + "moment-timezone": { + "version": "0.5.27", + "resolved": "https://registry.npmjs.org/moment-timezone/-/moment-timezone-0.5.27.tgz", + "integrity": "sha512-EIKQs7h5sAsjhPCqN6ggx6cEbs94GK050254TIJySD1bzoM5JTYDwAU1IoVOeTOL6Gm27kYJ51/uuvq1kIlrbw==", + "requires": { + "moment": ">= 2.9.0" + } + }, "moo": { "version": "0.4.3", "resolved": "https://registry.npmjs.org/moo/-/moo-0.4.3.tgz", @@ -10101,6 +10436,11 @@ "wrappy": "1" } }, + "one-time": { + "version": "0.0.4", + "resolved": "https://registry.npmjs.org/one-time/-/one-time-0.0.4.tgz", + "integrity": "sha1-+M33eISCb+Tf+T46nMN7HkSAdC4=" + }, "onetime": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/onetime/-/onetime-2.0.1.tgz", @@ -11780,6 +12120,21 @@ "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.2.tgz", "integrity": "sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0=" }, + "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==" + } + } + }, "sisteransi": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.2.tgz", @@ -12074,6 +12429,11 @@ "figgy-pudding": "^3.5.1" } }, + "stack-trace": { + "version": "0.0.10", + "resolved": "https://registry.npmjs.org/stack-trace/-/stack-trace-0.0.10.tgz", + "integrity": "sha1-VHxws0fo0ytOEI6hoqFZ5f3eGcA=" + }, "stack-utils": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/stack-utils/-/stack-utils-1.0.2.tgz", @@ -12494,6 +12854,11 @@ "resolved": "https://registry.npmjs.org/text-extensions/-/text-extensions-2.0.0.tgz", "integrity": "sha512-F91ZqLgvi1E0PdvmxMgp+gcf6q8fMH7mhdwWfzXnl1k+GbpQDmi8l7DzLC5JTASKbwpY3TfxajAUzAXcv2NmsQ==" }, + "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==" + }, "thenify": { "version": "3.3.0", "resolved": "https://registry.npmjs.org/thenify/-/thenify-3.3.0.tgz", @@ -12668,6 +13033,11 @@ "resolved": "https://registry.npmjs.org/trim-right/-/trim-right-1.0.1.tgz", "integrity": "sha1-yy4SAwZ+DI3h9hQJS5/kVwTqYAM=" }, + "triple-beam": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/triple-beam/-/triple-beam-1.3.0.tgz", + "integrity": "sha512-XrHUvV5HpdLmIj4uVMxHggLbFSZYIn7HEWsqePZcI50pco+MPqJ50wMGY794X7AOOhxOBAjbkqfAbEe/QMp2Lw==" + }, "ts-jest": { "version": "24.0.2", "resolved": "https://registry.npmjs.org/ts-jest/-/ts-jest-24.0.2.tgz", @@ -13399,6 +13769,43 @@ "execa": "^1.0.0" } }, + "winston": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/winston/-/winston-3.2.1.tgz", + "integrity": "sha512-zU6vgnS9dAWCEKg/QYigd6cgMVVNwyTzKs81XZtTFuRwJOcDdBg7AU0mXVyNbs7O5RH2zdv+BdNZUlx7mXPuOw==", + "requires": { + "async": "^2.6.1", + "diagnostics": "^1.1.1", + "is-stream": "^1.1.0", + "logform": "^2.1.1", + "one-time": "0.0.4", + "readable-stream": "^3.1.1", + "stack-trace": "0.0.x", + "triple-beam": "^1.3.0", + "winston-transport": "^4.3.0" + }, + "dependencies": { + "readable-stream": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.4.0.tgz", + "integrity": "sha512-jItXPLmrSR8jmTRmRWJXCnGJsfy85mB3Wd/uINMXA65yrnFo0cPClFIUWzo2najVNSl+mx7/4W8ttlLWJe99pQ==", + "requires": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + } + } + } + }, + "winston-transport": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/winston-transport/-/winston-transport-4.3.0.tgz", + "integrity": "sha512-B2wPuwUi3vhzn/51Uukcao4dIduEiPOcOt9HJ3QeaXgkJ5Z7UwpBzxS4ZGNHtrxrUvTwemsQiSys0ihOf8Mp1A==", + "requires": { + "readable-stream": "^2.3.6", + "triple-beam": "^1.2.0" + } + }, "wordwrap": { "version": "0.0.3", "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-0.0.3.tgz", diff --git a/package.json b/package.json index 41c2068..a764687 100644 --- a/package.json +++ b/package.json @@ -17,16 +17,16 @@ "@material-ui/icons": "^4.2.1", "@material-ui/lab": "^4.0.0-alpha.24", "@material-ui/styles": "4.3.0", - "@types/express": "^4.17.0", - "@types/marked": "^0.6.5", "assert-plus": "^1.0.0", "axios": "^0.19.0", "clsx": "1.0.4", "express": "^4.17.1", "immer": "^3.2.0", "inversify": "^5.0.1", + "inversify-express-utils": "^6.3.2", "marked": "^0.7.0", "material-table": "^1.49.0", + "moment-timezone": "^0.5.27", "next": "9.0.3", "next-i18next": "^0.54.0", "next-redux-saga": "^4.0.2", @@ -43,7 +43,8 @@ "remove": "^0.1.5", "tsconfig-paths": "^3.8.0", "typesafe-actions": "^4.4.2", - "uuid": "^3.3.3" + "uuid": "^3.3.3", + "winston": "^3.2.1" }, "devDependencies": { "@babel/plugin-proposal-class-properties": "^7.5.5", @@ -52,7 +53,10 @@ "@types/axios": "^0.14.0", "@types/enzyme": "^3.10.3", "@types/enzyme-adapter-react-16": "^1.0.5", + "@types/express": "^4.17.0", "@types/jest": "^24.0.16", + "@types/marked": "^0.6.5", + "@types/moment-timezone": "^0.5.12", "@types/next-redux-saga": "^3.0.1", "@types/node": "^12.6.9", "@types/react": "16.8.24", @@ -61,6 +65,7 @@ "@types/redux-form": "^8.1.4", "@types/redux-logger": "^3.0.7", "@types/uuid": "^3.4.5", + "@types/winston": "^2.4.4", "enzyme": "^3.10.0", "enzyme-adapter-react-16": "^1.14.0", "jest": "^24.8.0", diff --git a/server/index.ts b/server/index.ts index c80532c..86d861c 100644 --- a/server/index.ts +++ b/server/index.ts @@ -1,25 +1 @@ -import express from 'express'; -import next from 'next'; -import nextI18NextMiddleware from 'next-i18next/middleware'; -import inversifyServices from '../src/inversifyServices'; -import registerControllers from './registerControllers'; - -const nextI18next = inversifyServices.common.i18NService; - -export const PORT = process.env.PORT || 3000; -export const APP = next({ dev: process.env.NODE_ENV !== 'production' }); -export const SERVER = express(); -export const handle = APP.getRequestHandler(); - -APP.prepare().then(() => { - SERVER.use(nextI18NextMiddleware(nextI18next)); - - registerControllers(APP, SERVER); - - SERVER.get('*', (req, res) => handle(req, res)); - - SERVER.listen(PORT); - - // tslint:disable-next-line: no-console - console.log(`> Ready on http://localhost:${PORT}`); -}) \ No newline at end of file +import "./main" \ No newline at end of file diff --git a/server/main/common/error/ApiError.ts b/server/main/common/error/ApiError.ts new file mode 100644 index 0000000..24acff2 --- /dev/null +++ b/server/main/common/error/ApiError.ts @@ -0,0 +1,15 @@ +import moment from 'moment-timezone'; +import { logger } from "../utils"; + +export class ApiError { + public timestamp: string; + + public constructor( + public status: number, + public error: string, + public message: string, + ) { + this.timestamp = moment().tz("Asia/Seoul").toISOString(true); + logger.log('error', JSON.stringify(this)); + } +} \ No newline at end of file diff --git a/server/main/common/error/DefaultErrorHandler.ts b/server/main/common/error/DefaultErrorHandler.ts new file mode 100644 index 0000000..573e9d7 --- /dev/null +++ b/server/main/common/error/DefaultErrorHandler.ts @@ -0,0 +1,17 @@ +import { AssertionError } from "assert-plus"; +import { ErrorRequestHandler } from "express-serve-static-core"; +import { logger } from "../utils"; +import { ApiError } from "./ApiError"; + +export const defaultErrorHandler: ErrorRequestHandler = (err: Error, _, res, next) => { + if (res.headersSent) { + return next(err) + } + + if (err instanceof AssertionError) { + res.status(400).send(new ApiError(400, "Bad Request", err.message)); + } + + res.status(500).send(err.stack); + logger.log('error', JSON.stringify(err.stack)); +} \ No newline at end of file diff --git a/server/main/common/inversify/createApp.ts b/server/main/common/inversify/createApp.ts new file mode 100644 index 0000000..1a50d22 --- /dev/null +++ b/server/main/common/inversify/createApp.ts @@ -0,0 +1,30 @@ +import * as bodyParser from 'body-parser'; +import { ErrorRequestHandler } from 'express-serve-static-core'; +import { Container } from 'inversify'; +import { InversifyExpressServer } from 'inversify-express-utils'; +import nextI18NextMiddleware from 'next-i18next/middleware'; +import Optional from 'optional-js'; +import I18NService from 'src/common/service/I18NService'; +import "../../mother/notice/api/NoticeController"; +import { defaultErrorHandler } from "../error/DefaultErrorHandler"; +import { NextApp } from '../nextjs/NextApp'; + +export const createApp = (container: Container, errorHandlers?: ErrorRequestHandler[]) => new InversifyExpressServer(container) + .setConfig((theApp) => { + theApp.use(bodyParser.urlencoded({ extended: true })); + theApp.use(bodyParser.json()); + + theApp.use(nextI18NextMiddleware(I18NService)); + + }) + .setErrorConfig((theApp) => { + Optional.ofNullable(errorHandlers) + .map(handlers => handlers.forEach(h => theApp.use(h))); + + const handle = new NextApp().get().getRequestHandler(); + + theApp.get("*", (req, res) => handle(req, res)); + + theApp.use(defaultErrorHandler); + }) + .build(); \ No newline at end of file diff --git a/server/main/common/inversify/createContainer.ts b/server/main/common/inversify/createContainer.ts new file mode 100644 index 0000000..f98fd59 --- /dev/null +++ b/server/main/common/inversify/createContainer.ts @@ -0,0 +1,8 @@ +import { Container } from 'inversify'; +import { NextApp } from '../nextjs/NextApp'; + +export const createContainer = () => { + const container = new Container(); + container.bind('NextApp').to(NextApp); + return container; +} \ No newline at end of file diff --git a/server/main/common/nextjs/NextApp.ts b/server/main/common/nextjs/NextApp.ts new file mode 100644 index 0000000..f0f1fb3 --- /dev/null +++ b/server/main/common/nextjs/NextApp.ts @@ -0,0 +1,9 @@ +import { injectable } from 'inversify'; +import next from 'next'; + +const APP = next({ dev: process.env.NODE_ENV !== 'production' }); + +@injectable() +export class NextApp { + public get() { return APP; } +} \ No newline at end of file diff --git a/server/main/common/utils/__mocks__/logger.ts b/server/main/common/utils/__mocks__/logger.ts new file mode 100644 index 0000000..0fe6a4f --- /dev/null +++ b/server/main/common/utils/__mocks__/logger.ts @@ -0,0 +1,3 @@ +export const logger = { + log: jest.fn() +} \ No newline at end of file diff --git a/server/main/common/utils/index.ts b/server/main/common/utils/index.ts new file mode 100644 index 0000000..e45b535 --- /dev/null +++ b/server/main/common/utils/index.ts @@ -0,0 +1 @@ +export * from './logger' \ No newline at end of file diff --git a/server/main/common/utils/logger.ts b/server/main/common/utils/logger.ts new file mode 100644 index 0000000..362e23a --- /dev/null +++ b/server/main/common/utils/logger.ts @@ -0,0 +1,7 @@ +import winston from 'winston' + +export const logger = winston.createLogger({ + transports: [ + new winston.transports.Console() + ] +}); \ No newline at end of file diff --git a/server/main/errorHandlers.ts b/server/main/errorHandlers.ts new file mode 100644 index 0000000..6f09e7a --- /dev/null +++ b/server/main/errorHandlers.ts @@ -0,0 +1 @@ +export const errorHandlers = []; \ No newline at end of file diff --git a/server/main/index.ts b/server/main/index.ts new file mode 100644 index 0000000..e996749 --- /dev/null +++ b/server/main/index.ts @@ -0,0 +1,12 @@ +import "reflect-metadata"; + +import { createApp } from "./common/inversify/createApp"; +import { createContainer } from "./common/inversify/createContainer"; +import { logger } from "./common/utils"; +import { errorHandlers } from "./errorHandlers"; +import "./mother/notice/api/NoticeController"; + +const PORT = 3000; + +createApp(createContainer(), errorHandlers).listen(PORT); +logger.log("info", `server is running on port:${PORT}`); \ No newline at end of file diff --git a/server/main/mother/api/MotherController.ts b/server/main/mother/api/MotherController.ts new file mode 100644 index 0000000..51d35a6 --- /dev/null +++ b/server/main/mother/api/MotherController.ts @@ -0,0 +1,20 @@ +import { Request, Response } from "express"; +import { inject } from "inversify"; +import { controller, httpGet, interfaces, request, response } from "inversify-express-utils"; +import { NextApp } from "server/main/common/nextjs/NextApp"; + +const PATH = "/mother" + +@controller(PATH) +export class MotherController implements interfaces.Controller { + + constructor(@inject("NextApp") private nextApp: NextApp) { } + + @httpGet("/") + public get( + @request() req: Request, + @response() res: Response, + ) { + this.nextApp.get().render(req, res, PATH) + } +} \ No newline at end of file diff --git a/server/main/mother/notice/api/NoticeController.ts b/server/main/mother/notice/api/NoticeController.ts new file mode 100644 index 0000000..427392b --- /dev/null +++ b/server/main/mother/notice/api/NoticeController.ts @@ -0,0 +1,46 @@ +import { Request, Response } from "express"; +import { inject } from "inversify"; +import { controller, httpGet, interfaces, request, requestParam, response } from "inversify-express-utils"; +import { NextApp } from "server/main/common/nextjs/NextApp"; + +const PATH = "/mother/notice"; + +@controller(PATH) +export class ImageController implements interfaces.Controller { + + constructor(@inject("NextApp") private nextApp: NextApp) { } + + @httpGet("/add") + public add( + @request() req: Request, + @response() res: Response, + ) { + this.nextApp.get().render(req, res, `${PATH}/add`); + } + + @httpGet("/edit/:id") + public edit( + @request() req: Request, + @response() res: Response, + @requestParam("id") id: string, + ) { + this.nextApp.get().render(req, res, `${PATH}/form`, { id }); + } + + @httpGet("/:id") + public detail( + @request() req: Request, + @response() res: Response, + @requestParam("id") id: string, + ) { + this.nextApp.get().render(req, res, `${PATH}/detail`, { id }); + } + + @httpGet("/") + public index( + @request() req: Request, + @response() res: Response, + ) { + this.nextApp.get().render(req, res, PATH); + } +} \ No newline at end of file