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