diff --git a/.env b/.env index 83ebdaaa7b09c7d7d020bde04293cbb67915c785..0efeaa73b19403be4d206de6b8274dfc447b4926 100644 --- a/.env +++ b/.env @@ -1 +1,2 @@ -PORT=8000 \ No newline at end of file +PORT=8000 +INSTANCE=PROD \ No newline at end of file diff --git a/.gitignore b/.gitignore index e72bc483a43ba048045ad7faa318dabaa4253745..c6bdbb76309d8f805e24c9ab3df8f1ffde34d075 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,5 @@ node_modules build npm-debug.log -.DS_Store \ No newline at end of file +.DS_Store +logs/ diff --git a/package-lock.json b/package-lock.json index c007368bdf7e4b6b8f117743c73420c19a09c580..7b430bf65ec95b867c05df5dfc1a041350cc15a4 100644 --- a/package-lock.json +++ b/package-lock.json @@ -547,6 +547,16 @@ "@cspotcode/source-map-consumer": "0.8.0" } }, + "@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" + } + }, "@eslint/eslintrc": { "version": "0.4.3", "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-0.4.3.tgz", @@ -1123,6 +1133,21 @@ "@types/serve-static": "*" } }, + "@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==", + "dev": true, + "requires": { + "winston": "*" + } + }, + "@types/yamljs": { + "version": "0.2.31", + "resolved": "https://registry.npmjs.org/@types/yamljs/-/yamljs-0.2.31.tgz", + "integrity": "sha512-QcJ5ZczaXAqbVD3o8mw/mEBhRvO5UAdTtbvgwL/OgoWubvNBh6/MxLBAigtcgIFaq3shon9m3POIxQaLQt4fxQ==", + "dev": true + }, "@types/yargs": { "version": "16.0.4", "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-16.0.4.tgz", @@ -2056,6 +2081,15 @@ "integrity": "sha512-iBPtljfCNcTKNAto0KEtDfZ3qzjJvqE3aTGZsbhjSBlorqpXJlaWWtPO35D+ZImoC3KWejX64o+yPGxhWSTzfg==", "dev": true }, + "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", @@ -2069,6 +2103,15 @@ "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=" }, + "color-string": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/color-string/-/color-string-1.6.0.tgz", + "integrity": "sha512-c/hGS+kRWJutUBEngKKmk4iH3sD59MBkoxVapS/0wgpCz2u7XsNloxknyvBhzwEs1IbV36D9PwqLPJ2DTu3vMA==", + "requires": { + "color-name": "^1.0.0", + "simple-swizzle": "^0.2.2" + } + }, "color-support": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/color-support/-/color-support-1.1.3.tgz", @@ -2080,6 +2123,20 @@ "integrity": "sha512-ecORCqbSFP7Wm8Y6lyqMJjexBQqXSF7SSeaTyGGphogUjBlFP9m9o08wy86HL2uB7fMTxtOUzLMk7ogKcxMg1w==", "dev": true }, + "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" + } + }, "combined-stream": { "version": "1.0.8", "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", @@ -2178,6 +2235,11 @@ "integrity": "sha512-2VV7DlIbooyTI7Bh+yzOOWL9tGwLnQKHno7qATE+fqZzDKYr6llVjVQOzpD/QLZFgXDPb8T71pJokHEZHEYJhQ==", "dev": true }, + "core-util-is": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz", + "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==" + }, "cors": { "version": "2.8.5", "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.5.tgz", @@ -2456,6 +2518,11 @@ "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==" }, + "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", @@ -3222,6 +3289,11 @@ "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", "integrity": "sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=" }, + "fast-safe-stringify": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/fast-safe-stringify/-/fast-safe-stringify-2.1.1.tgz", + "integrity": "sha512-W+KJc2dmILlPplD/H4K9l9LcAHAfPtP6BY84uVLXQ6Evcz9Lcg33Y2z1IVblT6xdY54PXYVHEv+0Wpq8Io6zkA==" + }, "fb-watchman": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/fb-watchman/-/fb-watchman-2.0.1.tgz", @@ -3231,6 +3303,11 @@ "bser": "2.1.1" } }, + "fecha": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/fecha/-/fecha-4.2.1.tgz", + "integrity": "sha512-MMMQ0ludy/nBs1/o0zVOiKTpG7qMbonKUzjJgQFEuvq6INZ1OraKPRAWkBq5vlKLOUMpmNYG1JoN3oDPUQ9m3Q==" + }, "fflate": { "version": "0.3.11", "resolved": "https://registry.npmjs.org/fflate/-/fflate-0.3.11.tgz", @@ -3333,6 +3410,11 @@ "resolved": "https://registry.npmjs.org/flatted/-/flatted-2.0.2.tgz", "integrity": "sha512-r5wGx7YeOwNWNlCA0wQ86zKyDLMQr+/RB8xy74M4hTphfmjlijTSSXGuH8rnvKZnfT9i+75zmd8jcKdMR4O6jA==" }, + "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==" + }, "form-data": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/form-data/-/form-data-3.0.1.tgz", @@ -3805,6 +3887,11 @@ "integrity": "sha512-UqBRqi4ju7T+TqGNdqAO0PaSVGsDGJUBQvk9eUWNGRY1CFGDzYhLWoM7JQEemnlvVcv/YEmc2wNW8BC24EnUsw==", "dev": true }, + "helmet": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/helmet/-/helmet-4.6.0.tgz", + "integrity": "sha512-HVqALKZlR95ROkrnesdhbbZJFi/rIVSoNq6f3jA/9u6MIbTsPh3xZwihjeI5+DO/2sOV6HMHooXcEOuwskHpTg==" + }, "hosted-git-info": { "version": "2.8.9", "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.9.tgz", @@ -4398,8 +4485,7 @@ "is-stream": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", - "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", - "dev": true + "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==" }, "is-string": { "version": "1.0.7", @@ -4440,8 +4526,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", @@ -5156,6 +5241,11 @@ "integrity": "sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==", "dev": true }, + "kuler": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/kuler/-/kuler-2.0.0.tgz", + "integrity": "sha512-Xq9nH7KlWZmXAtodXDDRE7vs6DU1gTU8zYDHDiWLSip45Egwq3plLHzPn27NgvzL2r1LMPC1vdqh98sQxtqj4A==" + }, "language-subtag-registry": { "version": "0.3.21", "resolved": "https://registry.npmjs.org/language-subtag-registry/-/language-subtag-registry-0.3.21.tgz", @@ -5363,6 +5453,18 @@ } } }, + "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" + } + }, "loose-envify": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", @@ -5723,6 +5825,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": "5.1.2", "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", @@ -6010,6 +6120,11 @@ "resolved": "https://registry.npmjs.org/printj/-/printj-1.1.2.tgz", "integrity": "sha512-zA2SmoLaxZyArQTOPj5LXecR+RagfPSU5Kw1qP+jkWeNlrq+eJZyY2oS68SU1Z/7/myXM4lo9716laOFAVStCQ==" }, + "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==" + }, "progress": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz", @@ -6160,6 +6275,16 @@ "read-pkg": "^2.0.0" } }, + "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" + } + }, "readdirp": { "version": "3.6.0", "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", @@ -6439,6 +6564,21 @@ "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.3.tgz", "integrity": "sha512-VUJ49FC8U1OxwZLxIbTTrDvLnf/6TDgxZcK8wxR8zs13xpx7xbG60ndBlhNrFi2EMuFRoeDoJO7wthSLq42EjA==" }, + "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.5", "resolved": "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.5.tgz", @@ -6549,6 +6689,11 @@ "frac": "~1.1.2" } }, + "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": "2.0.3", "resolved": "https://registry.npmjs.org/stack-utils/-/stack-utils-2.0.3.tgz", @@ -6633,6 +6778,21 @@ "define-properties": "^1.1.3" } }, + "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==" + } + } + }, "stringify-object": { "version": "3.3.0", "resolved": "https://registry.npmjs.org/stringify-object/-/stringify-object-3.3.0.tgz", @@ -6858,6 +7018,11 @@ "minimatch": "^3.0.4" } }, + "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==" + }, "text-table": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", @@ -6948,6 +7113,11 @@ "punycode": "^2.1.1" } }, + "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-node": { "version": "10.2.1", "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.2.1.tgz", @@ -7221,6 +7391,11 @@ "prepend-http": "^2.0.0" } }, + "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", @@ -7366,6 +7541,62 @@ "string-width": "^4.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" + }, + "dependencies": { + "async": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/async/-/async-3.2.1.tgz", + "integrity": "sha512-XdD5lRO/87udXCMC9meWdYiR+Nq6ZjUfXidViUZGu2F1MO4T3XwZ1et0hb2++BgLfhyJwy44BGB/yx80ABx8hg==" + } + } + }, + "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" + } + } + } + }, "wmf": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/wmf/-/wmf-1.0.2.tgz", @@ -7514,6 +7745,15 @@ "integrity": "sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==", "dev": true }, + "yamljs": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/yamljs/-/yamljs-0.3.0.tgz", + "integrity": "sha512-C/FsVVhht4iPQYXOInoxUM/1ELSf9EsgKH34FofQOp6hwCPrW4vG4w5++TED3xRUo8gD7l0P1J1dLlDYzODsTQ==", + "requires": { + "argparse": "^1.0.7", + "glob": "^7.0.5" + } + }, "yargs": { "version": "16.2.0", "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", diff --git a/package.json b/package.json index 12c97c56e5ee0fa24dc0e943f12e56b418236c62..985296ea3b0886f726419db47b99c6c858cfc649 100644 --- a/package.json +++ b/package.json @@ -9,20 +9,25 @@ "dotenv": "^10.0.0", "express": "^4.17.1", "gulp-eslint": "^6.0.0", + "helmet": "^4.6.0", "openapi-types": "^9.3.0", "swagger-jsdoc": "^6.1.0", "swagger-ui-express": "^4.1.6", "tsconfig.json": "^1.0.10", - "xlsx": "^0.17.1" + "winston": "^3.3.3", + "xlsx": "^0.17.1", + "yamljs": "^0.3.0" }, "devDependencies": { "@types/cors": "^2.8.12", "@types/express": "^4.17.13", "@types/node": "^16.7.10", - "@typescript-eslint/eslint-plugin": "^2.34.0", - "@typescript-eslint/parser": "^2.0.0", "@types/swagger-jsdoc": "^6.0.1", "@types/swagger-ui-express": "^4.1.3", + "@types/winston": "^2.4.4", + "@types/yamljs": "^0.2.31", + "@typescript-eslint/eslint-plugin": "^2.34.0", + "@typescript-eslint/parser": "^2.0.0", "eslint": "^7.32.0", "eslint-config-airbnb": "^18.2.1", "eslint-config-airbnb-base": "^14.2.1", diff --git a/src/Config/Constants.ts b/src/Config/Constants.ts new file mode 100644 index 0000000000000000000000000000000000000000..6f2b9755a81eaa92b7ead75db327b4710888f7f0 --- /dev/null +++ b/src/Config/Constants.ts @@ -0,0 +1,6 @@ +export const SheetNames = { + HOMBRES: 'Hombres', + MUJERES: 'Mujeres', + HOMBRES_MENORES: 'Hombres<1', + MUJERES_MENORES: 'Mujeres<1', +}; diff --git a/src/Controllers/SheetController.ts b/src/Controllers/SheetController.ts index 2e298eabad83df8eee965779993c03593f0adb87..8d784ecbba54fb03dfdd96c6b93631d317229422 100644 --- a/src/Controllers/SheetController.ts +++ b/src/Controllers/SheetController.ts @@ -1,52 +1,24 @@ -import { - Handler, Request, Response, Router, -} from 'express'; -import { SheetParserResponse } from '../Models/SheetParserResponse'; -import SheetService from '../Services/SheetService'; - -const router = Router(); - -const parseSheet: Handler = async (req: Request, res: Response) => { - const sheet: Buffer = req.body; - try { - const parsedSheet: SheetParserResponse = SheetService.parseSheetService(sheet); - return res.status(200).send(parsedSheet); - } catch (error) { - const e = error as Error; - return res.status(400).json({ error: e.message }); - } -}; - -/** - * @swagger - * /excelParser: - * post: - * tags: - * - parser - * description: Sheet Parser - * requestBody: - * required: true - * content: - * application/json: - * schema: - * type: object - * required: - * - email - * - password - * properties: - * excel: - * type: string - * responses: - * '200': - * description: returns the parsed JSON of the excel file provided - * content: - * application/json: - * schema: - * type: object - * properties: - * excelParsed: - * type: string - */ -router.post('/', parseSheet); - -export default router; +import { + Handler, Request, Response, Router, +} from 'express'; +import { SheetParserResponse } from '../Models/SheetParserResponse'; +import SheetService from '../Services/SheetService'; +import logger from '../Logger/logger'; + +const router = Router(); + +const parseSheet: Handler = async (req: Request, res: Response) => { + const sheet: Buffer = req.body; + try { + const parsedSheet: SheetParserResponse = SheetService.parseSheetService(sheet); + return res.status(200).send(parsedSheet); + } catch (error) { + const e = error as Error; + logger.info(e.message); + return res.status(400).json({ error: e.message }); + } +}; + +router.post('/', parseSheet); + +export default router; diff --git a/src/Logger/logger.ts b/src/Logger/logger.ts new file mode 100644 index 0000000000000000000000000000000000000000..17715492a1df299bada6501b3b1a713c91957b09 --- /dev/null +++ b/src/Logger/logger.ts @@ -0,0 +1,32 @@ +import { + createLogger, format, transports, Logger, +} from 'winston'; + +const { + combine, timestamp, +} = format; + +const logger: Logger = createLogger({ + level: 'info', + format: combine( + timestamp(), + format.errors({ stack: true }), + format.json(), + ), + transports: [ + new transports.File({ + level: 'error', + filename: 'logs/error-logs', + maxsize: 104857600, // 100Mb + maxFiles: 3, + }), + new transports.File({ + level: 'info', + filename: 'logs/info-logs', + maxsize: 104857600, // 100Mb + maxFiles: 3, + }), + ], +}); + +export default logger; diff --git a/src/Services/SheetService.ts b/src/Services/SheetService.ts index 035452a01fb9a0926ca76e41111d609d1c26c924..947b1b0998d3b74ea107177cfbbd297d41962c12 100644 --- a/src/Services/SheetService.ts +++ b/src/Services/SheetService.ts @@ -1,143 +1,108 @@ -import * as XLSX from 'xlsx'; -import { - SheetParserResponse, Menores, Mayores, MenoresSheet, MayoresSheet, -} from '../Models/SheetParserResponse'; - -/* PRIVATE FUNCTIONS */ -// const ec = (r: number, c: number): string => XLSX.utils.encode_cell({ r, c }); -// const deleteRow = (ws: XLSX.WorkSheet, rowIndex: number): XLSX.WorkSheet => { -// const work = ws; -// if (work['!ref'] === undefined) throw new Error('An error has ocurred in deleteRow'); -// const variable = XLSX.utils.decode_range(work['!ref']); -// for (let R = rowIndex; R < variable.e.r; R += 1) { -// for (let C = variable.s.c; C <= variable.e.c; C += 1) { -// work[ec(R, C)] = work[ec(R + 1, C)]; -// } -// } -// variable.e.r -= 1; -// work['!ref'] = XLSX.utils.encode_range(variable.s, variable.e); -// return work; -// }; - -const parseMen = (worksheet: XLSX.WorkSheet): Mayores[] => { - const res: Mayores[] = []; - const ref = worksheet['!ref']; - if (ref === undefined) throw new Error('An error ocurred'); - const range = XLSX.utils.decode_range(ref); - range.s.c = 0; - range.e.c = 2; - const newRange = XLSX.utils.encode_range(range); - - const aux = XLSX.utils.sheet_to_json(worksheet, { range: newRange }) as unknown as MayoresSheet[]; - - aux.forEach((element: MayoresSheet) => { - res.push( - { - edad: element['Edad (años)'], - peso: element['Peso (Kg)'], - talla: element['Talla (cm)'], - }, - ); - }); - return res; -}; - -const parseWomen = (worksheet: XLSX.WorkSheet): Mayores[] => { - const res: Mayores[] = []; - const ref = worksheet['!ref']; - if (ref === undefined) throw new Error('An error ocurred'); - const range = XLSX.utils.decode_range(ref); - range.s.c = 0; - range.e.c = 2; - const newRange = XLSX.utils.encode_range(range); - - const aux = XLSX.utils.sheet_to_json(worksheet, { range: newRange }) as unknown as MayoresSheet[]; - aux.forEach((element: MayoresSheet) => { - res.push( - { - edad: element['Edad (años)'], - peso: element['Peso (Kg)'], - talla: element['Talla (cm)'], - }, - ); - }); - return res; -}; - -const parseMenLessThanAYear = (worksheet: XLSX.WorkSheet): Menores[] => { - const res: Menores[] = []; - const aux = XLSX.utils.sheet_to_json(worksheet) as unknown as MenoresSheet[]; - aux.forEach((element: MenoresSheet) => { - res.push( - { - edad: element['Edad (meses)'], - peso: element['Peso (Kg)'], - }, - ); - }); - return res; -}; - -const parseWomenLessThanAYear = (worksheet: XLSX.WorkSheet): Menores[] => { - const res: Menores[] = []; - const aux = XLSX.utils.sheet_to_json(worksheet) as unknown as MenoresSheet[]; - aux.forEach((element: MenoresSheet) => { - res.push( - { - edad: element['Edad (meses)'], - peso: element['Peso (Kg)'], - }, - ); - }); - return res; -}; - -/* EXPORT FUNCTIONS */ - -const parseSheetService = (data: Buffer): SheetParserResponse => { - const workbook: XLSX.WorkBook = XLSX.read(data); - let parsed: SheetParserResponse = null; - let hombresMenores: Menores[] = []; - let hombres: Mayores[] = []; - let mujeresMenores: Menores[] = []; - let mujeres: Mayores[] = []; - - const sheetNames: string[] = workbook.SheetNames; - // Check there are 4 sheets, no more, no less - if (sheetNames.length !== 4) { - throw new Error('File does not respect scheme, there are more or less than 4 sheets'); - } - sheetNames.forEach((name) => { - const worksheet: XLSX.WorkSheet = workbook.Sheets[name]; - switch (name) { - case 'Hombres<1': - hombresMenores = parseMenLessThanAYear(worksheet); - break; - case 'Hombres': - hombres = parseMen(worksheet); - break; - case 'Mujeres<1': - mujeresMenores = parseWomenLessThanAYear(worksheet); - break; - case 'Mujeres': - mujeres = parseWomen(worksheet); - break; - - default: - throw new Error(`Sheet name ${name} is not part of the scheme `); - } - }); - parsed = { - hombresMenores, - hombres, - mujeresMenores, - mujeres, - }; - - return parsed; - // TODO: depends on sheet layout what to do -}; - -export default { - parseSheetService, -}; +import * as XLSX from 'xlsx'; +import { SheetNames } from '../Config/Constants'; +import { + SheetParserResponse, Menores, Mayores, MenoresSheet, MayoresSheet, +} from '../Models/SheetParserResponse'; + +/* PRIVATE FUNCTIONS */ +// const ec = (r: number, c: number): string => XLSX.utils.encode_cell({ r, c }); +// const deleteRow = (ws: XLSX.WorkSheet, rowIndex: number): XLSX.WorkSheet => { +// const work = ws; +// if (work['!ref'] === undefined) throw new Error('An error has ocurred in deleteRow'); +// const variable = XLSX.utils.decode_range(work['!ref']); +// for (let R = rowIndex; R < variable.e.r; R += 1) { +// for (let C = variable.s.c; C <= variable.e.c; C += 1) { +// work[ec(R, C)] = work[ec(R + 1, C)]; +// } +// } +// variable.e.r -= 1; +// work['!ref'] = XLSX.utils.encode_range(variable.s, variable.e); +// return work; +// }; + +const parseAdults = (worksheet: XLSX.WorkSheet): Mayores[] => { + const res: Mayores[] = []; + const ref = worksheet['!ref']; + if (ref === undefined) throw new Error('An error ocurred'); + const range = XLSX.utils.decode_range(ref); + range.s.c = 0; + range.e.c = 2; + const newRange = XLSX.utils.encode_range(range); + + const aux = XLSX.utils.sheet_to_json(worksheet, { range: newRange }) as unknown as MayoresSheet[]; + + aux.forEach((element: MayoresSheet) => { + res.push( + { + edad: element['Edad (años)'], + peso: element['Peso (Kg)'], + talla: element['Talla (cm)'], + }, + ); + }); + return res; +}; + +const parseBabies = (worksheet: XLSX.WorkSheet): Menores[] => { + const res: Menores[] = []; + const aux = XLSX.utils.sheet_to_json(worksheet) as unknown as MenoresSheet[]; + aux.forEach((element: MenoresSheet) => { + res.push( + { + edad: element['Edad (meses)'], + peso: element['Peso (Kg)'], + }, + ); + }); + return res; +}; + +/* EXPORT FUNCTIONS */ + +const parseSheetService = (data: Buffer): SheetParserResponse => { + const workbook: XLSX.WorkBook = XLSX.read(data); + let parsed: SheetParserResponse = null; + let hombresMenores: Menores[] = []; + let hombres: Mayores[] = []; + let mujeresMenores: Menores[] = []; + let mujeres: Mayores[] = []; + + const sheetNames: string[] = workbook.SheetNames; + // Check there are 4 sheets, no more, no less + if (sheetNames.length !== 4) { + throw new Error('File does not respect scheme, there are more or less than 4 sheets'); + } + sheetNames.forEach((name) => { + const worksheet: XLSX.WorkSheet = workbook.Sheets[name]; + switch (name) { + case SheetNames.HOMBRES_MENORES: + hombresMenores = parseBabies(worksheet); + break; + case SheetNames.HOMBRES: + hombres = parseAdults(worksheet); + break; + case SheetNames.MUJERES_MENORES: + mujeresMenores = parseBabies(worksheet); + break; + case SheetNames.MUJERES: + mujeres = parseAdults(worksheet); + break; + + default: + throw new Error(`Sheet name ${name} is not part of the scheme `); + } + }); + parsed = { + hombresMenores, + hombres, + mujeresMenores, + mujeres, + }; + + return parsed; + // TODO: depends on sheet layout what to do +}; + +export default { + parseSheetService, +}; diff --git a/src/Services/test.xlsx b/src/Services/test.xlsx index 9fef8e5821051fe4270710bcec7daf0e25d1894a..3edb2bfeb00af26dd42279a2a4ebbdd2beb50724 100644 Binary files a/src/Services/test.xlsx and b/src/Services/test.xlsx differ diff --git a/src/index.ts b/src/index.ts index 18ca99641af256dff1c9124dfe8db221d31d945c..30dad1b806cb360a6b93e61524f8477ef8616086 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,49 +1,39 @@ -/* eslint-disable no-console */ -import express, { Application } from 'express'; -import 'dotenv/config'; -import cors from 'cors'; -import swaggerJsDoc, { Options } from 'swagger-jsdoc'; -import swaggerUi from 'swagger-ui-express'; -import Routes from './routes'; - -const app: Application = express(); -const PORT = process.env.PORT || 8000; - -// swagger init -const swaggerOptions: Options = { - swaggerDefinition: { - openapi: '3.0.0', - info: { - title: 'REPP Rest API', - version: '1.0.0', - description: '', - servers: ['http://localhost:3000'], - }, - }, - apis: ['src/routes.ts'], -}; -const swaggerDocs = swaggerJsDoc(swaggerOptions); - -// middlewares -app.use('/api-docs', swaggerUi.serve, swaggerUi.setup(swaggerDocs)); - -app.use(express.json({ - limit: '50mb', -})); -app.use(express.urlencoded({ extended: false })); -app.use(cors({ - origin: '*', - methods: 'GET,HEAD,PUT,PATCH,POST,DELETE', - preflightContinue: false, - optionsSuccessStatus: 204, -})); - -app.use(express.raw({ - limit: '50mb', -})); - -app.use(Routes); - -app.listen(PORT, (): void => { - console.log(`REPP Backend running here 👉 https://localhost:${PORT}`); -}); +/* eslint-disable no-console */ +import express, { Application } from 'express'; +import 'dotenv/config'; +import cors from 'cors'; +import swaggerUi from 'swagger-ui-express'; +import helmet from 'helmet'; +import YAML from 'yamljs'; +import Routes from './routes'; +import logger from './Logger/logger'; + +const app: Application = express(); +const PORT = process.env.PORT || 8000; +app.use(helmet.hidePoweredBy()); +// swagger init +const swaggerDocument = YAML.load('./swagger.yaml'); +// middlewares +app.use('/api-docs', swaggerUi.serve, swaggerUi.setup(swaggerDocument)); + +app.use(express.json({ + limit: '50mb', +})); +app.use(express.urlencoded({ extended: false })); +app.use(cors({ + origin: '*', + methods: 'GET,HEAD,PUT,PATCH,POST,DELETE', + preflightContinue: false, + optionsSuccessStatus: 204, +})); + +app.use(express.raw({ + limit: '50mb', +})); + +app.use(Routes); + +app.listen(PORT, (): void => { + console.log(`REPP Backend running here 👉 https://localhost:${PORT}`); + logger.info('Server initiated'); +}); diff --git a/swagger.yaml b/swagger.yaml new file mode 100644 index 0000000000000000000000000000000000000000..b424dc77f3b74ba186b84b58830e3557723265d0 --- /dev/null +++ b/swagger.yaml @@ -0,0 +1,90 @@ +openapi: 3.0.0 +info: + version: 1.0.0 + title: REPP Backend + description: '' +security: + - BearerAuth: [] +tags: + - name: Auth + - name: Parser + - name: Calculation +paths: + + /login: + post: + tags: + - Auth + summary: Login to get an access token + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/UserCredentials' + + responses: + '200': + description: Ok. + security: [] + + /sheetParser: + post: + tags: + - Parser + summary: Given a sheet returns SheetParserResponse + requestBody: + content: + application/octet-stream: + schema: + format: binary + required: true + responses: + '200': + description: Ok. + security: + - BearerAuth: [] +components: + securitySchemes: + BearerAuth: + type: http + scheme: bearer + schemas: + Menores: + properties: + edad: + type: integer + peso: + type: integer + Mayores: + properties: + edad: + type: integer + peso: + type: integer + talla: + type: integer + SheetParserResponse: + properties: + hombresMenores: + type: array + items: + $ref: '#/components/schemas/Menores' + mujeresMenores: + type: array + items: + $ref: '#/components/schemas/Menores' + hombres: + type: array + items: + $ref: '#/components/schemas/Mayores' + mujeres: + type: array + items: + $ref: '#/components/schemas/Mayores' + UserCredentials: + properties: + user: + type: string + pass: + type: string