diff --git a/.gitignore b/.gitignore index 2ece8f7..d469135 100644 --- a/.gitignore +++ b/.gitignore @@ -7,4 +7,5 @@ prisma/migrations/dev client dist pnpm-lock.yaml -package_backup.json \ No newline at end of file +package_backup.json +logs/ \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index 3ce55e5..9ffa7a7 100644 --- a/package-lock.json +++ b/package-lock.json @@ -13,16 +13,20 @@ "aws-sdk": "^2.1414.0", "bcrypt": "^5.1.0", "compression": "^1.7.4", + "cors": "^2.8.5", "dotenv": "^16.3.1", "express": "^4.18.2", "express-rate-limit": "^6.7.1", "ioredis": "^5.3.2", "jsonwebtoken": "^9.0.0", + "morgan": "^1.10.0", "multer": "^1.4.5-lts.1", "multer-s3": "^3.0.1", "rate-limit-redis": "^3.0.2", "sharp": "^0.32.3", - "validator": "^13.9.0" + "socket.io": "^4.7.2", + "validator": "^13.9.0", + "winston": "^3.10.0" }, "devDependencies": { "@faker-js/faker": "^8.0.2", @@ -31,10 +35,12 @@ "@swc/jest": "^0.2.26", "@types/bcrypt": "^5.0.0", "@types/compression": "^1.7.2", + "@types/cors": "^2.8.13", "@types/dotenv": "^8.2.0", "@types/express": "^4.17.17", "@types/jest": "^29.5.2", "@types/jsonwebtoken": "^9.0.2", + "@types/morgan": "^1.9.4", "@types/multer-s3": "^3.0.0", "@types/node": "^20.3.1", "@types/supertest": "^2.0.12", @@ -1711,6 +1717,14 @@ "dev": true, "license": "MIT" }, + "node_modules/@colors/colors": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@colors/colors/-/colors-1.5.0.tgz", + "integrity": "sha512-ooWCrlZP11i8GImSjTHYHLkvFDP48nS4+204nGb1RiX/WXYHmJA2III9/e2DWVabCESdW7hBAEzHRqUn9OUVvQ==", + "engines": { + "node": ">=0.1.90" + } + }, "node_modules/@cspotcode/source-map-support": { "version": "0.8.1", "dev": true, @@ -1731,6 +1745,16 @@ "@jridgewell/sourcemap-codec": "^1.4.10" } }, + "node_modules/@dabh/diagnostics": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@dabh/diagnostics/-/diagnostics-2.0.3.tgz", + "integrity": "sha512-hrlQOIi7hAfzsMqlGSFyVucrx38O+j6wiGOf//H2ecvIEqYN4ADBSS2iLMh5UFyDunCNniUIPk/q3riFv45xRA==", + "dependencies": { + "colorspace": "1.1.x", + "enabled": "2.0.x", + "kuler": "^2.0.0" + } + }, "node_modules/@eslint-community/eslint-utils": { "version": "4.4.0", "dev": true, @@ -3369,6 +3393,11 @@ "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.0.tgz", "integrity": "sha512-7At1WUettjcSRHXCyYtTselblcHl9PJFFVKiCAy/bY97+BPZXSQ2wbq0P9s8tK2G7dFQfNnlJnPAiArVBVBsfA==" }, + "node_modules/@socket.io/component-emitter": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@socket.io/component-emitter/-/component-emitter-3.1.0.tgz", + "integrity": "sha512-+9jVqKhRSpsc591z5vX+X5Yyw+he/HCB4iQ/RYxw35CEPaY1gnsNE43nf9n9AaYjAQrTiI/mOwKUKdUs9vf7Xg==" + }, "node_modules/@swc/cli": { "version": "0.1.62", "dev": true, @@ -3607,11 +3636,24 @@ "@types/node": "*" } }, + "node_modules/@types/cookie": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/@types/cookie/-/cookie-0.4.1.tgz", + "integrity": "sha512-XW/Aa8APYr6jSVVA1y/DEIZX0/GMKLEVekNG727R8cs56ahETkRAy/3DR7+fJyh7oUgGwNQaRfXCun0+KbWY7Q==" + }, "node_modules/@types/cookiejar": { "version": "2.1.2", "dev": true, "license": "MIT" }, + "node_modules/@types/cors": { + "version": "2.8.13", + "resolved": "https://registry.npmjs.org/@types/cors/-/cors-2.8.13.tgz", + "integrity": "sha512-RG8AStHlUiV5ysZQKq97copd2UmVYw3/pRMLefISZ3S1hK104Cwm7iLQ3fTKx+lsUH2CE8FlLaYeEA2LSeqYUA==", + "dependencies": { + "@types/node": "*" + } + }, "node_modules/@types/dotenv": { "version": "8.2.0", "dev": true, @@ -3723,6 +3765,15 @@ "dev": true, "license": "MIT" }, + "node_modules/@types/morgan": { + "version": "1.9.4", + "resolved": "https://registry.npmjs.org/@types/morgan/-/morgan-1.9.4.tgz", + "integrity": "sha512-cXoc4k+6+YAllH3ZHmx4hf7La1dzUk6keTR4bF4b4Sc0mZxU/zK4wO7l+ZzezXm/jkYj/qC+uYGZrarZdIVvyQ==", + "dev": true, + "dependencies": { + "@types/node": "*" + } + }, "node_modules/@types/multer": { "version": "1.4.7", "resolved": "https://registry.npmjs.org/@types/multer/-/multer-1.4.7.tgz", @@ -3744,7 +3795,6 @@ }, "node_modules/@types/node": { "version": "20.3.1", - "dev": true, "license": "MIT" }, "node_modules/@types/prettier": { @@ -3827,6 +3877,11 @@ "@types/superagent": "*" } }, + "node_modules/@types/triple-beam": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/@types/triple-beam/-/triple-beam-1.3.2.tgz", + "integrity": "sha512-txGIh+0eDFzKGC25zORnswy+br1Ha7hj5cMVwKIU7+s0U2AxxJru/jZSMU6OC9MJWP6+pc/hc6ZjyZShpsyY2g==" + }, "node_modules/@types/validator": { "version": "13.7.17", "dev": true, @@ -4392,8 +4447,7 @@ "node_modules/async": { "version": "3.2.4", "resolved": "https://registry.npmjs.org/async/-/async-3.2.4.tgz", - "integrity": "sha512-iAB+JbDEGXhyIUavoDl9WP/Jj106Kz9DEn1DPgYw5ruDn0e3Wgi3sKFm55sASdGBNOQB8F59d9qQ7deqrHA8wQ==", - "dev": true + "integrity": "sha512-iAB+JbDEGXhyIUavoDl9WP/Jj106Kz9DEn1DPgYw5ruDn0e3Wgi3sKFm55sASdGBNOQB8F59d9qQ7deqrHA8wQ==" }, "node_modules/async-listener": { "version": "0.6.10", @@ -4593,6 +4647,25 @@ } ] }, + "node_modules/base64id": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/base64id/-/base64id-2.0.0.tgz", + "integrity": "sha512-lGe34o6EHj9y3Kts9R4ZYs/Gr+6N7MCaMlIFA3F1R2O5/m7K06AxfSeO5530PEERE6/WyEg3lsuyw4GHlPZHog==", + "engines": { + "node": "^4.5.0 || >= 5.9" + } + }, + "node_modules/basic-auth": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/basic-auth/-/basic-auth-2.0.1.tgz", + "integrity": "sha512-NF+epuEdnUYVlGuhaxbbq+dvJttwLnGY+YixlXlME5KpQ5W3CnXA5cVTneY3SPbPDRkcjMbifrwmFYcClgOZeg==", + "dependencies": { + "safe-buffer": "5.1.2" + }, + "engines": { + "node": ">= 0.8" + } + }, "node_modules/bcrypt": { "version": "5.1.0", "hasInstallScript": true, @@ -5265,6 +5338,37 @@ "color-support": "bin.js" } }, + "node_modules/colorspace": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/colorspace/-/colorspace-1.1.4.tgz", + "integrity": "sha512-BgvKJiuVu1igBUF2kEjRCZXol6wiiGbY5ipL/oVPwm0BL9sIpMIzM8IK7vwuxIIzOXMV3Ey5w+vxhm0rR/TN8w==", + "dependencies": { + "color": "^3.1.3", + "text-hex": "1.0.x" + } + }, + "node_modules/colorspace/node_modules/color": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/color/-/color-3.2.1.tgz", + "integrity": "sha512-aBl7dZI9ENN6fUGC7mWpMTPNHmWUSNan9tuWN6ahh5ZLNk9baLJOnSMlrQkHcrfFgz2/RigjUVAjdx36VcemKA==", + "dependencies": { + "color-convert": "^1.9.3", + "color-string": "^1.6.0" + } + }, + "node_modules/colorspace/node_modules/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==", + "dependencies": { + "color-name": "1.1.3" + } + }, + "node_modules/colorspace/node_modules/color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==" + }, "node_modules/combined-stream": { "version": "1.0.8", "dev": true, @@ -5442,6 +5546,18 @@ "version": "1.0.3", "license": "MIT" }, + "node_modules/cors": { + "version": "2.8.5", + "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.5.tgz", + "integrity": "sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==", + "dependencies": { + "object-assign": "^4", + "vary": "^1" + }, + "engines": { + "node": ">= 0.10" + } + }, "node_modules/create-require": { "version": "1.1.1", "dev": true, @@ -5748,6 +5864,11 @@ "version": "8.0.0", "license": "MIT" }, + "node_modules/enabled": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/enabled/-/enabled-2.0.0.tgz", + "integrity": "sha512-AKrN98kuwOzMIdAizXGI86UFBoo26CL21UM763y1h/GMSJ4/OHU9k2YlsmBpyScFo/wbLzWQJBMCW4+IO3/+OQ==" + }, "node_modules/encodeurl": { "version": "1.0.2", "license": "MIT", @@ -5762,6 +5883,62 @@ "once": "^1.4.0" } }, + "node_modules/engine.io": { + "version": "6.5.2", + "resolved": "https://registry.npmjs.org/engine.io/-/engine.io-6.5.2.tgz", + "integrity": "sha512-IXsMcGpw/xRfjra46sVZVHiSWo/nJ/3g1337q9KNXtS6YRzbW5yIzTCb9DjhrBe7r3GZQR0I4+nq+4ODk5g/cA==", + "dependencies": { + "@types/cookie": "^0.4.1", + "@types/cors": "^2.8.12", + "@types/node": ">=10.0.0", + "accepts": "~1.3.4", + "base64id": "2.0.0", + "cookie": "~0.4.1", + "cors": "~2.8.5", + "debug": "~4.3.1", + "engine.io-parser": "~5.2.1", + "ws": "~8.11.0" + }, + "engines": { + "node": ">=10.2.0" + } + }, + "node_modules/engine.io-parser": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-5.2.1.tgz", + "integrity": "sha512-9JktcM3u18nU9N2Lz3bWeBgxVgOKpw7yhRaoxQA3FUDZzzw+9WlA6p4G4u0RixNkg14fH7EfEc/RhpurtiROTQ==", + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/engine.io/node_modules/cookie": { + "version": "0.4.2", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.2.tgz", + "integrity": "sha512-aSWTXFzaKWkvHO1Ny/s+ePFpvKsPnjc551iI41v3ny/ow6tBG5Vd+FuqGNhh1LxOmVzOlGUriIlOaokOvhaStA==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/engine.io/node_modules/ws": { + "version": "8.11.0", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.11.0.tgz", + "integrity": "sha512-HPG3wQd9sNQoT9xHyNCXoDUa+Xw/VevmY9FoHyQ+g+rrMn4j6FB4np7Z0OhdTgjx6MgQLK7jwSy1YecU1+4Asg==", + "engines": { + "node": ">=10.0.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": "^5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + }, "node_modules/enquirer": { "version": "2.3.6", "dev": true, @@ -6692,6 +6869,11 @@ "integrity": "sha512-GDqVQezKzRABdeqflsgMr7ktzgF9CyS+p2oe0jJqUY6izSSbhPIQJDpoU4PtGcD7VPM9xh/dVrTu6z1nwgmEGw==", "dev": true }, + "node_modules/fecha": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/fecha/-/fecha-4.2.3.tgz", + "integrity": "sha512-OP2IUU6HeYKJi3i0z4A19kHMQoLVs4Hc+DPqqxI2h/DPZHTm/vjsfC6P0b4jCMy14XizLBqvndQ+UilD7707Jw==" + }, "node_modules/file-entry-cache": { "version": "6.0.1", "dev": true, @@ -6839,6 +7021,11 @@ "dev": true, "license": "ISC" }, + "node_modules/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==" + }, "node_modules/follow-redirects": { "version": "1.15.2", "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.2.tgz", @@ -7801,7 +7988,6 @@ }, "node_modules/is-stream": { "version": "2.0.1", - "dev": true, "license": "MIT", "engines": { "node": ">=8" @@ -8621,6 +8807,11 @@ "node": ">=6" } }, + "node_modules/kuler": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/kuler/-/kuler-2.0.0.tgz", + "integrity": "sha512-Xq9nH7KlWZmXAtodXDDRE7vs6DU1gTU8zYDHDiWLSip45Egwq3plLHzPn27NgvzL2r1LMPC1vdqh98sQxtqj4A==" + }, "node_modules/lazy": { "version": "1.0.11", "resolved": "https://registry.npmjs.org/lazy/-/lazy-1.0.11.tgz", @@ -8702,6 +8893,19 @@ "node": ">=0.8.6" } }, + "node_modules/logform": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/logform/-/logform-2.5.1.tgz", + "integrity": "sha512-9FyqAm9o9NKKfiAKfZoYo9bGXXuwMkxQiQttkT4YjjVtQVIQtK6LmVtlxmCaFswo6N4AfEkHqZTV0taDtPotNg==", + "dependencies": { + "@colors/colors": "1.5.0", + "@types/triple-beam": "^1.3.2", + "fecha": "^4.2.0", + "ms": "^2.1.1", + "safe-stable-stringify": "^2.3.1", + "triple-beam": "^1.3.0" + } + }, "node_modules/lowercase-keys": { "version": "2.0.0", "dev": true, @@ -8929,6 +9133,45 @@ "node": "*" } }, + "node_modules/morgan": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/morgan/-/morgan-1.10.0.tgz", + "integrity": "sha512-AbegBVI4sh6El+1gNwvD5YIck7nSA36weD7xvIxG4in80j/UoK8AEGaWnnz8v1GxonMCltmlNs5ZKbGvl9b1XQ==", + "dependencies": { + "basic-auth": "~2.0.1", + "debug": "2.6.9", + "depd": "~2.0.0", + "on-finished": "~2.3.0", + "on-headers": "~1.0.2" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/morgan/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/morgan/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" + }, + "node_modules/morgan/node_modules/on-finished": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz", + "integrity": "sha512-ikqdkGAAyf/X/gPhXGvfgAytDZtDbr+bkNUJ0N9h5MI/dmdgCs3l6hoHrcUv41sRKew3jIwrp4qQDXiK99Utww==", + "dependencies": { + "ee-first": "1.1.1" + }, + "engines": { + "node": ">= 0.8" + } + }, "node_modules/ms": { "version": "2.1.2", "license": "MIT" @@ -9328,6 +9571,14 @@ "wrappy": "1" } }, + "node_modules/one-time": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/one-time/-/one-time-1.0.0.tgz", + "integrity": "sha512-5DXOiRKwuSEcQ/l0kGCF6Q3jcADFv5tSmRaJck/OqkVFcOzutB134KRSfF0xDrL39MNnqxbHBbUUcjZIhTgb2g==", + "dependencies": { + "fn.name": "1.x.x" + } + }, "node_modules/onetime": { "version": "5.1.2", "dev": true, @@ -10417,6 +10668,14 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/safe-stable-stringify": { + "version": "2.4.3", + "resolved": "https://registry.npmjs.org/safe-stable-stringify/-/safe-stable-stringify-2.4.3.tgz", + "integrity": "sha512-e2bDA2WJT0wxseVd4lsDP4+3ONX6HpMXQa1ZhFQ7SU+GjvORCmShbCMltrtIDfkYhVHrOcPtj+KhmDBdPdZD1g==", + "engines": { + "node": ">=10" + } + }, "node_modules/safer-buffer": { "version": "2.1.2", "license": "MIT" @@ -10701,6 +10960,63 @@ "npm": ">= 3.0.0" } }, + "node_modules/socket.io": { + "version": "4.7.2", + "resolved": "https://registry.npmjs.org/socket.io/-/socket.io-4.7.2.tgz", + "integrity": "sha512-bvKVS29/I5fl2FGLNHuXlQaUH/BlzX1IN6S+NKLNZpBsPZIDH+90eQmCs2Railn4YUiww4SzUedJ6+uzwFnKLw==", + "dependencies": { + "accepts": "~1.3.4", + "base64id": "~2.0.0", + "cors": "~2.8.5", + "debug": "~4.3.2", + "engine.io": "~6.5.2", + "socket.io-adapter": "~2.5.2", + "socket.io-parser": "~4.2.4" + }, + "engines": { + "node": ">=10.2.0" + } + }, + "node_modules/socket.io-adapter": { + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/socket.io-adapter/-/socket.io-adapter-2.5.2.tgz", + "integrity": "sha512-87C3LO/NOMc+eMcpcxUBebGjkpMDkNBS9tf7KJqcDsmL936EChtVva71Dw2q4tQcuVC+hAUy4an2NO/sYXmwRA==", + "dependencies": { + "ws": "~8.11.0" + } + }, + "node_modules/socket.io-adapter/node_modules/ws": { + "version": "8.11.0", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.11.0.tgz", + "integrity": "sha512-HPG3wQd9sNQoT9xHyNCXoDUa+Xw/VevmY9FoHyQ+g+rrMn4j6FB4np7Z0OhdTgjx6MgQLK7jwSy1YecU1+4Asg==", + "engines": { + "node": ">=10.0.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": "^5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + }, + "node_modules/socket.io-parser": { + "version": "4.2.4", + "resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-4.2.4.tgz", + "integrity": "sha512-/GbIKmo8ioc+NIWIhwdecY0ge+qVBSMdgxGygevmdHj24bsfgtCmcUUcQ5ZzcylGFHsN3k4HB4Cgkl96KVnuew==", + "dependencies": { + "@socket.io/component-emitter": "~3.1.0", + "debug": "~4.3.1" + }, + "engines": { + "node": ">=10.0.0" + } + }, "node_modules/socks": { "version": "2.7.1", "resolved": "https://registry.npmjs.org/socks/-/socks-2.7.1.tgz", @@ -10779,6 +11095,14 @@ "dev": true, "license": "BSD-3-Clause" }, + "node_modules/stack-trace": { + "version": "0.0.10", + "resolved": "https://registry.npmjs.org/stack-trace/-/stack-trace-0.0.10.tgz", + "integrity": "sha512-KGzahc7puUKkzyMt+IqAep+TVNbKP+k2Lmwhub39m1AsTSkaDutx56aDCo+HLDzf/D26BIHTJWNiTG1KAJiQCg==", + "engines": { + "node": "*" + } + }, "node_modules/stack-utils": { "version": "2.0.6", "dev": true, @@ -11124,6 +11448,11 @@ "node": ">=8" } }, + "node_modules/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==" + }, "node_modules/text-table": { "version": "0.2.0", "dev": true, @@ -11232,6 +11561,14 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/triple-beam": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/triple-beam/-/triple-beam-1.4.1.tgz", + "integrity": "sha512-aZbgViZrg1QNcG+LULa7nhZpJTZSLm/mXnHXnbAbjmN5aSa0y7V+wvv6+4WaBtpISJzThKy+PIPxc1Nq1EJ9mg==", + "engines": { + "node": ">= 14.0.0" + } + }, "node_modules/ts-jest": { "version": "29.1.0", "dev": true, @@ -11832,6 +12169,40 @@ "string-width": "^1.0.2 || 2 || 3 || 4" } }, + "node_modules/winston": { + "version": "3.10.0", + "resolved": "https://registry.npmjs.org/winston/-/winston-3.10.0.tgz", + "integrity": "sha512-nT6SIDaE9B7ZRO0u3UvdrimG0HkB7dSTAgInQnNR2SOPJ4bvq5q79+pXLftKmP52lJGW15+H5MCK0nM9D3KB/g==", + "dependencies": { + "@colors/colors": "1.5.0", + "@dabh/diagnostics": "^2.0.2", + "async": "^3.2.3", + "is-stream": "^2.0.0", + "logform": "^2.4.0", + "one-time": "^1.0.0", + "readable-stream": "^3.4.0", + "safe-stable-stringify": "^2.3.1", + "stack-trace": "0.0.x", + "triple-beam": "^1.3.0", + "winston-transport": "^4.5.0" + }, + "engines": { + "node": ">= 12.0.0" + } + }, + "node_modules/winston-transport": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/winston-transport/-/winston-transport-4.5.0.tgz", + "integrity": "sha512-YpZzcUzBedhlTAfJg6vJDlyEai/IFMIVcaEZZyl3UXIl4gmqRpU7AE89AHLkbzLUsv0NVmw7ts+iztqKxxPW1Q==", + "dependencies": { + "logform": "^2.3.2", + "readable-stream": "^3.6.0", + "triple-beam": "^1.3.0" + }, + "engines": { + "node": ">= 6.4.0" + } + }, "node_modules/word-wrap": { "version": "1.2.3", "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz", @@ -13438,6 +13809,11 @@ "version": "0.2.3", "dev": true }, + "@colors/colors": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@colors/colors/-/colors-1.5.0.tgz", + "integrity": "sha512-ooWCrlZP11i8GImSjTHYHLkvFDP48nS4+204nGb1RiX/WXYHmJA2III9/e2DWVabCESdW7hBAEzHRqUn9OUVvQ==" + }, "@cspotcode/source-map-support": { "version": "0.8.1", "dev": true, @@ -13455,6 +13831,16 @@ } } }, + "@dabh/diagnostics": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@dabh/diagnostics/-/diagnostics-2.0.3.tgz", + "integrity": "sha512-hrlQOIi7hAfzsMqlGSFyVucrx38O+j6wiGOf//H2ecvIEqYN4ADBSS2iLMh5UFyDunCNniUIPk/q3riFv45xRA==", + "requires": { + "colorspace": "1.1.x", + "enabled": "2.0.x", + "kuler": "^2.0.0" + } + }, "@eslint-community/eslint-utils": { "version": "4.4.0", "dev": true, @@ -14824,6 +15210,11 @@ } } }, + "@socket.io/component-emitter": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@socket.io/component-emitter/-/component-emitter-3.1.0.tgz", + "integrity": "sha512-+9jVqKhRSpsc591z5vX+X5Yyw+he/HCB4iQ/RYxw35CEPaY1gnsNE43nf9n9AaYjAQrTiI/mOwKUKdUs9vf7Xg==" + }, "@swc/cli": { "version": "0.1.62", "dev": true, @@ -14980,10 +15371,23 @@ "@types/node": "*" } }, + "@types/cookie": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/@types/cookie/-/cookie-0.4.1.tgz", + "integrity": "sha512-XW/Aa8APYr6jSVVA1y/DEIZX0/GMKLEVekNG727R8cs56ahETkRAy/3DR7+fJyh7oUgGwNQaRfXCun0+KbWY7Q==" + }, "@types/cookiejar": { "version": "2.1.2", "dev": true }, + "@types/cors": { + "version": "2.8.13", + "resolved": "https://registry.npmjs.org/@types/cors/-/cors-2.8.13.tgz", + "integrity": "sha512-RG8AStHlUiV5ysZQKq97copd2UmVYw3/pRMLefISZ3S1hK104Cwm7iLQ3fTKx+lsUH2CE8FlLaYeEA2LSeqYUA==", + "requires": { + "@types/node": "*" + } + }, "@types/dotenv": { "version": "8.2.0", "dev": true, @@ -15082,6 +15486,15 @@ "version": "1.3.2", "dev": true }, + "@types/morgan": { + "version": "1.9.4", + "resolved": "https://registry.npmjs.org/@types/morgan/-/morgan-1.9.4.tgz", + "integrity": "sha512-cXoc4k+6+YAllH3ZHmx4hf7La1dzUk6keTR4bF4b4Sc0mZxU/zK4wO7l+ZzezXm/jkYj/qC+uYGZrarZdIVvyQ==", + "dev": true, + "requires": { + "@types/node": "*" + } + }, "@types/multer": { "version": "1.4.7", "resolved": "https://registry.npmjs.org/@types/multer/-/multer-1.4.7.tgz", @@ -15102,8 +15515,7 @@ } }, "@types/node": { - "version": "20.3.1", - "dev": true + "version": "20.3.1" }, "@types/prettier": { "version": "2.7.3", @@ -15174,6 +15586,11 @@ "@types/superagent": "*" } }, + "@types/triple-beam": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/@types/triple-beam/-/triple-beam-1.3.2.tgz", + "integrity": "sha512-txGIh+0eDFzKGC25zORnswy+br1Ha7hj5cMVwKIU7+s0U2AxxJru/jZSMU6OC9MJWP6+pc/hc6ZjyZShpsyY2g==" + }, "@types/validator": { "version": "13.7.17", "dev": true @@ -15529,8 +15946,7 @@ "async": { "version": "3.2.4", "resolved": "https://registry.npmjs.org/async/-/async-3.2.4.tgz", - "integrity": "sha512-iAB+JbDEGXhyIUavoDl9WP/Jj106Kz9DEn1DPgYw5ruDn0e3Wgi3sKFm55sASdGBNOQB8F59d9qQ7deqrHA8wQ==", - "dev": true + "integrity": "sha512-iAB+JbDEGXhyIUavoDl9WP/Jj106Kz9DEn1DPgYw5ruDn0e3Wgi3sKFm55sASdGBNOQB8F59d9qQ7deqrHA8wQ==" }, "async-listener": { "version": "0.6.10", @@ -15673,6 +16089,19 @@ "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==" }, + "base64id": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/base64id/-/base64id-2.0.0.tgz", + "integrity": "sha512-lGe34o6EHj9y3Kts9R4ZYs/Gr+6N7MCaMlIFA3F1R2O5/m7K06AxfSeO5530PEERE6/WyEg3lsuyw4GHlPZHog==" + }, + "basic-auth": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/basic-auth/-/basic-auth-2.0.1.tgz", + "integrity": "sha512-NF+epuEdnUYVlGuhaxbbq+dvJttwLnGY+YixlXlME5KpQ5W3CnXA5cVTneY3SPbPDRkcjMbifrwmFYcClgOZeg==", + "requires": { + "safe-buffer": "5.1.2" + } + }, "bcrypt": { "version": "5.1.0", "requires": { @@ -16105,6 +16534,39 @@ "color-support": { "version": "1.1.3" }, + "colorspace": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/colorspace/-/colorspace-1.1.4.tgz", + "integrity": "sha512-BgvKJiuVu1igBUF2kEjRCZXol6wiiGbY5ipL/oVPwm0BL9sIpMIzM8IK7vwuxIIzOXMV3Ey5w+vxhm0rR/TN8w==", + "requires": { + "color": "^3.1.3", + "text-hex": "1.0.x" + }, + "dependencies": { + "color": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/color/-/color-3.2.1.tgz", + "integrity": "sha512-aBl7dZI9ENN6fUGC7mWpMTPNHmWUSNan9tuWN6ahh5ZLNk9baLJOnSMlrQkHcrfFgz2/RigjUVAjdx36VcemKA==", + "requires": { + "color-convert": "^1.9.3", + "color-string": "^1.6.0" + } + }, + "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==", + "requires": { + "color-name": "1.1.3" + } + }, + "color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==" + } + } + }, "combined-stream": { "version": "1.0.8", "dev": true, @@ -16236,6 +16698,15 @@ "core-util-is": { "version": "1.0.3" }, + "cors": { + "version": "2.8.5", + "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.5.tgz", + "integrity": "sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==", + "requires": { + "object-assign": "^4", + "vary": "^1" + } + }, "create-require": { "version": "1.1.1", "dev": true @@ -16431,6 +16902,11 @@ "emoji-regex": { "version": "8.0.0" }, + "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" }, @@ -16440,6 +16916,41 @@ "once": "^1.4.0" } }, + "engine.io": { + "version": "6.5.2", + "resolved": "https://registry.npmjs.org/engine.io/-/engine.io-6.5.2.tgz", + "integrity": "sha512-IXsMcGpw/xRfjra46sVZVHiSWo/nJ/3g1337q9KNXtS6YRzbW5yIzTCb9DjhrBe7r3GZQR0I4+nq+4ODk5g/cA==", + "requires": { + "@types/cookie": "^0.4.1", + "@types/cors": "^2.8.12", + "@types/node": ">=10.0.0", + "accepts": "~1.3.4", + "base64id": "2.0.0", + "cookie": "~0.4.1", + "cors": "~2.8.5", + "debug": "~4.3.1", + "engine.io-parser": "~5.2.1", + "ws": "~8.11.0" + }, + "dependencies": { + "cookie": { + "version": "0.4.2", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.2.tgz", + "integrity": "sha512-aSWTXFzaKWkvHO1Ny/s+ePFpvKsPnjc551iI41v3ny/ow6tBG5Vd+FuqGNhh1LxOmVzOlGUriIlOaokOvhaStA==" + }, + "ws": { + "version": "8.11.0", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.11.0.tgz", + "integrity": "sha512-HPG3wQd9sNQoT9xHyNCXoDUa+Xw/VevmY9FoHyQ+g+rrMn4j6FB4np7Z0OhdTgjx6MgQLK7jwSy1YecU1+4Asg==", + "requires": {} + } + } + }, + "engine.io-parser": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-5.2.1.tgz", + "integrity": "sha512-9JktcM3u18nU9N2Lz3bWeBgxVgOKpw7yhRaoxQA3FUDZzzw+9WlA6p4G4u0RixNkg14fH7EfEc/RhpurtiROTQ==" + }, "enquirer": { "version": "2.3.6", "dev": true, @@ -17101,6 +17612,11 @@ "integrity": "sha512-GDqVQezKzRABdeqflsgMr7ktzgF9CyS+p2oe0jJqUY6izSSbhPIQJDpoU4PtGcD7VPM9xh/dVrTu6z1nwgmEGw==", "dev": true }, + "fecha": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/fecha/-/fecha-4.2.3.tgz", + "integrity": "sha512-OP2IUU6HeYKJi3i0z4A19kHMQoLVs4Hc+DPqqxI2h/DPZHTm/vjsfC6P0b4jCMy14XizLBqvndQ+UilD7707Jw==" + }, "file-entry-cache": { "version": "6.0.1", "dev": true, @@ -17193,6 +17709,11 @@ "version": "3.2.7", "dev": true }, + "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.15.2", "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.2.tgz", @@ -17807,8 +18328,7 @@ } }, "is-stream": { - "version": "2.0.1", - "dev": true + "version": "2.0.1" }, "is-string": { "version": "1.0.7", @@ -18383,6 +18903,11 @@ "version": "3.0.3", "dev": true }, + "kuler": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/kuler/-/kuler-2.0.0.tgz", + "integrity": "sha512-Xq9nH7KlWZmXAtodXDDRE7vs6DU1gTU8zYDHDiWLSip45Egwq3plLHzPn27NgvzL2r1LMPC1vdqh98sQxtqj4A==" + }, "lazy": { "version": "1.0.11", "resolved": "https://registry.npmjs.org/lazy/-/lazy-1.0.11.tgz", @@ -18439,6 +18964,19 @@ "integrity": "sha512-U7KCmLdqsGHBLeWqYlFA0V0Sl6P08EE1ZrmA9cxjUE0WVqT9qnyVDPz1kzpFEP0jdJuFnasWIfSd7fsaNXkpbg==", "dev": true }, + "logform": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/logform/-/logform-2.5.1.tgz", + "integrity": "sha512-9FyqAm9o9NKKfiAKfZoYo9bGXXuwMkxQiQttkT4YjjVtQVIQtK6LmVtlxmCaFswo6N4AfEkHqZTV0taDtPotNg==", + "requires": { + "@colors/colors": "1.5.0", + "@types/triple-beam": "^1.3.2", + "fecha": "^4.2.0", + "ms": "^2.1.1", + "safe-stable-stringify": "^2.3.1", + "triple-beam": "^1.3.0" + } + }, "lowercase-keys": { "version": "2.0.0", "dev": true @@ -18578,6 +19116,41 @@ "moment": "^2.29.4" } }, + "morgan": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/morgan/-/morgan-1.10.0.tgz", + "integrity": "sha512-AbegBVI4sh6El+1gNwvD5YIck7nSA36weD7xvIxG4in80j/UoK8AEGaWnnz8v1GxonMCltmlNs5ZKbGvl9b1XQ==", + "requires": { + "basic-auth": "~2.0.1", + "debug": "2.6.9", + "depd": "~2.0.0", + "on-finished": "~2.3.0", + "on-headers": "~1.0.2" + }, + "dependencies": { + "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" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" + }, + "on-finished": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz", + "integrity": "sha512-ikqdkGAAyf/X/gPhXGvfgAytDZtDbr+bkNUJ0N9h5MI/dmdgCs3l6hoHrcUv41sRKew3jIwrp4qQDXiK99Utww==", + "requires": { + "ee-first": "1.1.1" + } + } + } + }, "ms": { "version": "2.1.2" }, @@ -18853,6 +19426,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", "dev": true, @@ -19556,6 +20137,11 @@ "is-regex": "^1.1.4" } }, + "safe-stable-stringify": { + "version": "2.4.3", + "resolved": "https://registry.npmjs.org/safe-stable-stringify/-/safe-stable-stringify-2.4.3.tgz", + "integrity": "sha512-e2bDA2WJT0wxseVd4lsDP4+3ONX6HpMXQa1ZhFQ7SU+GjvORCmShbCMltrtIDfkYhVHrOcPtj+KhmDBdPdZD1g==" + }, "safer-buffer": { "version": "2.1.2" }, @@ -19752,6 +20338,45 @@ "integrity": "sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg==", "dev": true }, + "socket.io": { + "version": "4.7.2", + "resolved": "https://registry.npmjs.org/socket.io/-/socket.io-4.7.2.tgz", + "integrity": "sha512-bvKVS29/I5fl2FGLNHuXlQaUH/BlzX1IN6S+NKLNZpBsPZIDH+90eQmCs2Railn4YUiww4SzUedJ6+uzwFnKLw==", + "requires": { + "accepts": "~1.3.4", + "base64id": "~2.0.0", + "cors": "~2.8.5", + "debug": "~4.3.2", + "engine.io": "~6.5.2", + "socket.io-adapter": "~2.5.2", + "socket.io-parser": "~4.2.4" + } + }, + "socket.io-adapter": { + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/socket.io-adapter/-/socket.io-adapter-2.5.2.tgz", + "integrity": "sha512-87C3LO/NOMc+eMcpcxUBebGjkpMDkNBS9tf7KJqcDsmL936EChtVva71Dw2q4tQcuVC+hAUy4an2NO/sYXmwRA==", + "requires": { + "ws": "~8.11.0" + }, + "dependencies": { + "ws": { + "version": "8.11.0", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.11.0.tgz", + "integrity": "sha512-HPG3wQd9sNQoT9xHyNCXoDUa+Xw/VevmY9FoHyQ+g+rrMn4j6FB4np7Z0OhdTgjx6MgQLK7jwSy1YecU1+4Asg==", + "requires": {} + } + } + }, + "socket.io-parser": { + "version": "4.2.4", + "resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-4.2.4.tgz", + "integrity": "sha512-/GbIKmo8ioc+NIWIhwdecY0ge+qVBSMdgxGygevmdHj24bsfgtCmcUUcQ5ZzcylGFHsN3k4HB4Cgkl96KVnuew==", + "requires": { + "@socket.io/component-emitter": "~3.1.0", + "debug": "~4.3.1" + } + }, "socks": { "version": "2.7.1", "resolved": "https://registry.npmjs.org/socks/-/socks-2.7.1.tgz", @@ -19811,6 +20436,11 @@ "version": "1.1.2", "dev": true }, + "stack-trace": { + "version": "0.0.10", + "resolved": "https://registry.npmjs.org/stack-trace/-/stack-trace-0.0.10.tgz", + "integrity": "sha512-KGzahc7puUKkzyMt+IqAep+TVNbKP+k2Lmwhub39m1AsTSkaDutx56aDCo+HLDzf/D26BIHTJWNiTG1KAJiQCg==" + }, "stack-utils": { "version": "2.0.6", "dev": true, @@ -20040,6 +20670,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", "dev": true @@ -20106,6 +20741,11 @@ } } }, + "triple-beam": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/triple-beam/-/triple-beam-1.4.1.tgz", + "integrity": "sha512-aZbgViZrg1QNcG+LULa7nhZpJTZSLm/mXnHXnbAbjmN5aSa0y7V+wvv6+4WaBtpISJzThKy+PIPxc1Nq1EJ9mg==" + }, "ts-jest": { "version": "29.1.0", "dev": true, @@ -20497,6 +21137,34 @@ "string-width": "^1.0.2 || 2 || 3 || 4" } }, + "winston": { + "version": "3.10.0", + "resolved": "https://registry.npmjs.org/winston/-/winston-3.10.0.tgz", + "integrity": "sha512-nT6SIDaE9B7ZRO0u3UvdrimG0HkB7dSTAgInQnNR2SOPJ4bvq5q79+pXLftKmP52lJGW15+H5MCK0nM9D3KB/g==", + "requires": { + "@colors/colors": "1.5.0", + "@dabh/diagnostics": "^2.0.2", + "async": "^3.2.3", + "is-stream": "^2.0.0", + "logform": "^2.4.0", + "one-time": "^1.0.0", + "readable-stream": "^3.4.0", + "safe-stable-stringify": "^2.3.1", + "stack-trace": "0.0.x", + "triple-beam": "^1.3.0", + "winston-transport": "^4.5.0" + } + }, + "winston-transport": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/winston-transport/-/winston-transport-4.5.0.tgz", + "integrity": "sha512-YpZzcUzBedhlTAfJg6vJDlyEai/IFMIVcaEZZyl3UXIl4gmqRpU7AE89AHLkbzLUsv0NVmw7ts+iztqKxxPW1Q==", + "requires": { + "logform": "^2.3.2", + "readable-stream": "^3.6.0", + "triple-beam": "^1.3.0" + } + }, "word-wrap": { "version": "1.2.3", "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz", diff --git a/package.json b/package.json index 05486ad..51864d0 100644 --- a/package.json +++ b/package.json @@ -36,10 +36,12 @@ "@swc/jest": "^0.2.26", "@types/bcrypt": "^5.0.0", "@types/compression": "^1.7.2", + "@types/cors": "^2.8.13", "@types/dotenv": "^8.2.0", "@types/express": "^4.17.17", "@types/jest": "^29.5.2", "@types/jsonwebtoken": "^9.0.2", + "@types/morgan": "^1.9.4", "@types/multer-s3": "^3.0.0", "@types/node": "^20.3.1", "@types/supertest": "^2.0.12", @@ -68,15 +70,19 @@ "aws-sdk": "^2.1414.0", "bcrypt": "^5.1.0", "compression": "^1.7.4", + "cors": "^2.8.5", "dotenv": "^16.3.1", "express": "^4.18.2", "express-rate-limit": "^6.7.1", "ioredis": "^5.3.2", "jsonwebtoken": "^9.0.0", + "morgan": "^1.10.0", "multer": "^1.4.5-lts.1", "multer-s3": "^3.0.1", "rate-limit-redis": "^3.0.2", "sharp": "^0.32.3", - "validator": "^13.9.0" + "socket.io": "^4.7.2", + "validator": "^13.9.0", + "winston": "^3.10.0" } } diff --git a/prisma/migrations/20230805193612_created_socket_id_field/migration.sql b/prisma/migrations/20230805193612_created_socket_id_field/migration.sql new file mode 100644 index 0000000..9c21756 --- /dev/null +++ b/prisma/migrations/20230805193612_created_socket_id_field/migration.sql @@ -0,0 +1,2 @@ +-- AlterTable +ALTER TABLE "User" ADD COLUMN "socketId" TEXT; diff --git a/prisma/schema.prisma b/prisma/schema.prisma index 72da87a..0ae848c 100644 --- a/prisma/schema.prisma +++ b/prisma/schema.prisma @@ -22,6 +22,7 @@ model User { postComments Comments[] fromNotifications Notifications[] @relation("fromNotifications") toNotifications Notifications[] @relation("toNotifications") + socketId String? createdAt DateTime @default(now()) } diff --git a/src/@types/express.d.ts b/src/@types/express.d.ts index 84c48a0..68030d2 100644 --- a/src/@types/express.d.ts +++ b/src/@types/express.d.ts @@ -1,11 +1,17 @@ /* eslint-disable */ import * as express from 'express' -import jwtPayload from '../interfaces/jwt' +import type jwtPayload from '../interfaces/jwt' declare global { namespace Express { interface Request { user: jwtPayload | undefined } + namespace Multer { + interface File { + location: string + key: string + } + } } } diff --git a/src/app.ts b/src/app.ts index 6ceb351..f87185b 100644 --- a/src/app.ts +++ b/src/app.ts @@ -1,19 +1,25 @@ import 'dotenv/config' -import express from 'express' -import router from './routes' import compression from 'compression' -import limiter from './middlewares/rate-limit' +import cors from 'cors' +import express from 'express' +import limiter from 'middlewares/rate-limit' +import morganMiddleware from 'middlewares/morgan' +import router from './routes' const app = express() -// TODO: Create a documentation page or create the client because this is getting really big - app.use(express.json()) app.use(express.urlencoded({ extended: true })) -app.use(router) +app.use(morganMiddleware) app.use(limiter) +app.use(router) app.use(compression({ level: 9 })) +app.use(cors({ + credentials: true, + origin: '*', // TODO: Add client development (and production too) url + optionsSuccessStatus: 200 +})) app.use((_req, res) => { res.status(404).json({ diff --git a/src/clients/s3-client.ts b/src/clients/s3-client.ts index bdbf870..2478967 100644 --- a/src/clients/s3-client.ts +++ b/src/clients/s3-client.ts @@ -1,9 +1,10 @@ import { S3Client } from '@aws-sdk/client-s3' +import logger from 'helpers/logger' let s3: S3Client if (process.env.NODE_ENV === 'development') { - console.log('Using Localstack services instead of AWS.') + logger.info('Using Localstack services instead of AWS.') s3 = new S3Client({ credentials: { accessKeyId: process.env.AWS_ACCESS_KEY_ID ?? '', diff --git a/src/controllers/comments/create.ts b/src/controllers/comments/create.ts index 01217e5..ea64159 100644 --- a/src/controllers/comments/create.ts +++ b/src/controllers/comments/create.ts @@ -1,6 +1,7 @@ import comment from 'services/comments' import type { Request, Response } from 'express' -import { badRequest } from 'lib/http-errors' +import { badRequest } from 'helpers/http-errors' +import handleResponse from 'helpers/handle-response' async function commentCreateController ( req: Request, @@ -19,11 +20,7 @@ async function commentCreateController ( const result = await comment.create(postId, content, id) - if (result instanceof Error) { - badRequest(res, result.message); return - } - - res.json(result) + handleResponse(res, result) } export default commentCreateController diff --git a/src/controllers/comments/delete.ts b/src/controllers/comments/delete.ts index 32f7b86..134910c 100644 --- a/src/controllers/comments/delete.ts +++ b/src/controllers/comments/delete.ts @@ -1,6 +1,7 @@ import comment from 'services/comments' import type { Request, Response } from 'express' -import { badRequest } from 'lib/http-errors' +import { badRequest } from 'helpers/http-errors' +import handleResponse from 'helpers/handle-response' async function commentDeleteController ( req: Request, @@ -15,11 +16,7 @@ async function commentDeleteController ( const result = await comment.delete(commentId, id) - if (result instanceof Error) { - badRequest(res, result.message); return - } - - res.json(result) + handleResponse(res, result) } export default commentDeleteController diff --git a/src/controllers/comments/fetch-info.ts b/src/controllers/comments/fetch-info.ts index 059229a..9af150b 100644 --- a/src/controllers/comments/fetch-info.ts +++ b/src/controllers/comments/fetch-info.ts @@ -1,6 +1,7 @@ import comment from 'services/comments' import type { Request, Response } from 'express' -import { badRequest } from 'lib/http-errors' +import { badRequest } from 'helpers/http-errors' +import handleResponse from 'helpers/handle-response' async function commentFetchController ( req: Request, @@ -14,11 +15,7 @@ async function commentFetchController ( const result = await comment.fetch(commentId) - if (result instanceof Error) { - badRequest(res, result.message); return - } - - res.json(result) + handleResponse(res, result) } export default commentFetchController diff --git a/src/controllers/comments/fetch-likes.ts b/src/controllers/comments/fetch-likes.ts index 19fcc01..5926d6d 100644 --- a/src/controllers/comments/fetch-likes.ts +++ b/src/controllers/comments/fetch-likes.ts @@ -1,6 +1,7 @@ import comment from 'services/comments' import type { Request, Response } from 'express' -import { badRequest } from 'lib/http-errors' +import { badRequest } from 'helpers/http-errors' +import handleResponse from 'helpers/handle-response' async function commentFetchLikesController ( req: Request, @@ -14,11 +15,7 @@ async function commentFetchLikesController ( const result = await comment.fetchLikes(commentId) - if (result instanceof Error) { - badRequest(res, result.message); return - } - - res.json(result) + handleResponse(res, result) } export default commentFetchLikesController diff --git a/src/controllers/comments/update.ts b/src/controllers/comments/update.ts index 1ca3bb2..ed0385d 100644 --- a/src/controllers/comments/update.ts +++ b/src/controllers/comments/update.ts @@ -1,6 +1,7 @@ import comment from 'services/comments' import type { Request, Response } from 'express' -import { badRequest } from 'lib/http-errors' +import { badRequest } from 'helpers/http-errors' +import handleResponse from 'helpers/handle-response' async function commentUpdateController ( req: Request, @@ -19,11 +20,7 @@ async function commentUpdateController ( const result = await comment.update(content, id, commentId) - if (result instanceof Error) { - badRequest(res, result.message); return - } - - res.json(result) + handleResponse(res, result) } export default commentUpdateController diff --git a/src/controllers/posts/create.ts b/src/controllers/posts/create.ts index 665bc56..ced4433 100644 --- a/src/controllers/posts/create.ts +++ b/src/controllers/posts/create.ts @@ -1,6 +1,7 @@ import post from 'services/posts' import type { Request, Response } from 'express' -import { badRequest } from 'lib/http-errors' +import { badRequest } from 'helpers/http-errors' +import handleResponse from 'helpers/handle-response' async function postCreateController ( req: Request, @@ -15,11 +16,7 @@ async function postCreateController ( const result = await post.create(content, id) - if (result instanceof Error) { - badRequest(res, result.message); return - } - - res.json(result) + handleResponse(res, result) } export default postCreateController diff --git a/src/controllers/posts/delete.ts b/src/controllers/posts/delete.ts index 4329827..0a7109e 100644 --- a/src/controllers/posts/delete.ts +++ b/src/controllers/posts/delete.ts @@ -1,6 +1,7 @@ import post from 'services/posts' import type { Request, Response } from 'express' -import { badRequest } from 'lib/http-errors' +import { badRequest } from 'helpers/http-errors' +import handleResponse from 'helpers/handle-response' async function postDeleteController ( req: Request, @@ -9,13 +10,13 @@ async function postDeleteController ( const userId = req.user?.id ?? '' const postId = req.body.postId - const result = await post.delete(postId, userId) - - if (result instanceof Error) { - badRequest(res, result.message); return + if (postId === undefined) { + badRequest(res, 'Missing post id'); return } - res.json(result) + const result = await post.delete(postId, userId) + + handleResponse(res, result) } export default postDeleteController diff --git a/src/controllers/posts/fetch-info.ts b/src/controllers/posts/fetch-info.ts index eda3275..5709e59 100644 --- a/src/controllers/posts/fetch-info.ts +++ b/src/controllers/posts/fetch-info.ts @@ -1,6 +1,7 @@ import post from 'services/posts' import type { Request, Response } from 'express' -import { badRequest } from 'lib/http-errors' +import { badRequest } from 'helpers/http-errors' +import handleResponse from 'helpers/handle-response' async function postFetchInfoController ( req: Request, @@ -14,11 +15,7 @@ async function postFetchInfoController ( const result = await post.fetch(id) - if (result instanceof Error) { - badRequest(res, result.message); return - } - - res.json(result) + handleResponse(res, result) } export default postFetchInfoController diff --git a/src/controllers/posts/fetch-likes.ts b/src/controllers/posts/fetch-likes.ts index 4c35daf..92c86e4 100644 --- a/src/controllers/posts/fetch-likes.ts +++ b/src/controllers/posts/fetch-likes.ts @@ -1,6 +1,7 @@ import post from 'services/posts' import type { Request, Response } from 'express' -import { badRequest } from 'lib/http-errors' +import { badRequest } from 'helpers/http-errors' +import handleResponse from 'helpers/handle-response' async function postFetchLikesController ( req: Request, @@ -14,11 +15,7 @@ async function postFetchLikesController ( const result = await post.fetchLikes(id) - if (result instanceof Error) { - badRequest(res, result.message); return - } - - res.json(result) + handleResponse(res, result) } export default postFetchLikesController diff --git a/src/controllers/posts/update.ts b/src/controllers/posts/update.ts index ae18caf..448700f 100644 --- a/src/controllers/posts/update.ts +++ b/src/controllers/posts/update.ts @@ -1,6 +1,6 @@ import post from 'services/posts' import type { Request, Response } from 'express' -import { badRequest } from 'lib/http-errors' +import handleResponse from 'helpers/handle-response' async function postUpdateController ( req: Request, @@ -11,11 +11,7 @@ async function postUpdateController ( const result = await post.update(postId, content, userId) - if (result instanceof Error) { - badRequest(res, result.message); return - } - - res.json(result) + handleResponse(res, result) } export default postUpdateController diff --git a/src/controllers/users/auth.ts b/src/controllers/users/auth.ts index b788b26..0d242ab 100644 --- a/src/controllers/users/auth.ts +++ b/src/controllers/users/auth.ts @@ -1,17 +1,13 @@ import user from 'services/users' import type { Request, Response } from 'express' -import { badRequest } from 'lib/http-errors' +import handleResponse from 'helpers/handle-response' async function userAuthController (req: Request, res: Response): Promise { const { email, password } = req.body const result = await user.auth({ email, password }) - if (result instanceof Error) { - badRequest(res, result.message); return - } - - res.json(result) + handleResponse(res, result) } export default userAuthController diff --git a/src/controllers/users/delete.ts b/src/controllers/users/delete.ts index 63c5211..908c2ec 100644 --- a/src/controllers/users/delete.ts +++ b/src/controllers/users/delete.ts @@ -1,6 +1,6 @@ import user from 'services/users' import type { Request, Response } from 'express' -import { badRequest } from 'lib/http-errors' +import handleResponse from 'helpers/handle-response' async function userDeleteController ( req: Request, @@ -9,11 +9,7 @@ async function userDeleteController ( const userId = req.user?.id ?? '' const result = await user.delete(userId) - if (result instanceof Error) { - badRequest(res, result.message); return - } - - res.json(result) + handleResponse(res, result) } export default userDeleteController diff --git a/src/controllers/users/fetch-info.ts b/src/controllers/users/fetch-info.ts index c9fe709..120cf8a 100644 --- a/src/controllers/users/fetch-info.ts +++ b/src/controllers/users/fetch-info.ts @@ -1,6 +1,7 @@ import user from 'services/users' import type { Request, Response } from 'express' -import { badRequest } from 'lib/http-errors' +import { badRequest } from 'helpers/http-errors' +import handleResponse from 'helpers/handle-response' async function userFetchInfoController ( req: Request, @@ -14,11 +15,7 @@ async function userFetchInfoController ( const result = await user.fetchInfo(username.toLowerCase()) - if (result instanceof Error) { - badRequest(res, result.message); return - } - - res.json(result) + handleResponse(res, result) } export default userFetchInfoController diff --git a/src/controllers/users/fetch-posts.ts b/src/controllers/users/fetch-posts.ts index ca4dea0..b12348d 100644 --- a/src/controllers/users/fetch-posts.ts +++ b/src/controllers/users/fetch-posts.ts @@ -1,6 +1,7 @@ import user from 'services/users' import type { Request, Response } from 'express' -import { badRequest } from 'lib/http-errors' +import { badRequest } from 'helpers/http-errors' +import handleResponse from 'helpers/handle-response' async function userFetchPostsController ( req: Request, @@ -14,7 +15,7 @@ async function userFetchPostsController ( const result = await user.fetchPosts(username) - res.json(result) + handleResponse(res, result) } export default userFetchPostsController diff --git a/src/controllers/users/follow-user.ts b/src/controllers/users/follow-user.ts index a089b3d..af34bd0 100644 --- a/src/controllers/users/follow-user.ts +++ b/src/controllers/users/follow-user.ts @@ -1,6 +1,6 @@ import user from 'services/users' import type { Request, Response } from 'express' -import { badRequest } from 'lib/http-errors' +import handleResponse from 'helpers/handle-response' async function userFollowController ( req: Request, @@ -11,11 +11,7 @@ async function userFollowController ( const result = await user.follow(userId, userToFollow) - if (result instanceof Error) { - badRequest(res, result.message); return - } - - res.json(result) + handleResponse(res, result) } export default userFollowController diff --git a/src/controllers/users/like-comment.ts b/src/controllers/users/like-comment.ts index 79fcd41..c7f367b 100644 --- a/src/controllers/users/like-comment.ts +++ b/src/controllers/users/like-comment.ts @@ -1,6 +1,6 @@ import user from 'services/users' import type { Request, Response } from 'express' -import { badRequest } from 'lib/http-errors' +import handleResponse from 'helpers/handle-response' async function userLikeCommentController ( req: Request, @@ -11,11 +11,7 @@ async function userLikeCommentController ( const result = await user.likeComment(commentId, userId) - if (result instanceof Error) { - badRequest(res, result.message); return - } - - res.json(result) + handleResponse(res, result) } export default userLikeCommentController diff --git a/src/controllers/users/like-post.ts b/src/controllers/users/like-post.ts index 2d72de1..1f3f40a 100644 --- a/src/controllers/users/like-post.ts +++ b/src/controllers/users/like-post.ts @@ -1,6 +1,6 @@ import user from 'services/users' import type { Request, Response } from 'express' -import { badRequest } from 'lib/http-errors' +import handleResponse from 'helpers/handle-response' async function userLikePostController ( req: Request, @@ -11,11 +11,7 @@ async function userLikePostController ( const result = await user.likePost(postId, userId) - if (result instanceof Error) { - badRequest(res, result.message); return - } - - res.json(result) + handleResponse(res, result) } export default userLikePostController diff --git a/src/controllers/users/search-user.ts b/src/controllers/users/search-user.ts index 2224457..9b3f723 100644 --- a/src/controllers/users/search-user.ts +++ b/src/controllers/users/search-user.ts @@ -1,6 +1,6 @@ import user from 'services/users' import type { Request, Response } from 'express' -import { badRequest } from 'lib/http-errors' +import { badRequest } from 'helpers/http-errors' async function userSearchController ( req: Request, diff --git a/src/controllers/users/signup.ts b/src/controllers/users/signup.ts index cbd68c2..3407b53 100644 --- a/src/controllers/users/signup.ts +++ b/src/controllers/users/signup.ts @@ -1,6 +1,6 @@ import user from 'services/users' import type { Request, Response } from 'express' -import { badRequest } from 'lib/http-errors' +import handleResponse from 'helpers/handle-response' async function userSignupController ( req: Request, @@ -10,11 +10,7 @@ async function userSignupController ( const result = await user.signup({ username, email, password }) - if (result instanceof Error) { - badRequest(res, result.message); return - } - - res.json(result) + handleResponse(res, result) } export default userSignupController diff --git a/src/controllers/users/update.ts b/src/controllers/users/update.ts index 2d4f6a6..9c61b0f 100644 --- a/src/controllers/users/update.ts +++ b/src/controllers/users/update.ts @@ -1,6 +1,6 @@ import user from 'services/users' import type { Request, Response } from 'express' -import { badRequest } from 'lib/http-errors' +import handleResponse from 'helpers/handle-response' async function userUpdateController ( req: Request, @@ -11,11 +11,7 @@ async function userUpdateController ( const result = await user.update({ id, email, displayName, username }) - if (result instanceof Error) { - badRequest(res, result.message); return - } - - res.json(result) + handleResponse(res, result) } export default userUpdateController diff --git a/src/controllers/users/upload-picture.ts b/src/controllers/users/upload-picture.ts index 3b8a736..ec263c3 100644 --- a/src/controllers/users/upload-picture.ts +++ b/src/controllers/users/upload-picture.ts @@ -1,9 +1,8 @@ /* eslint-disable @typescript-eslint/restrict-template-expressions */ import user from 'services/users' import type { Request, Response } from 'express' -import { badRequest } from 'lib/http-errors' - -let url +import { badRequest } from 'helpers/http-errors' +import handleResponse from 'helpers/handle-response' async function userUploadPictureController ( req: Request, @@ -15,21 +14,17 @@ async function userUploadPictureController ( const userId = req.user?.id ?? '' + let url: string + if (process.env.NODE_ENV === 'development') { - // @ts-expect-error property `key` doesn't exists in @types/express url = `http://${process.env.AWS_BUCKET ?? ''}.s3.localhost.localstack.cloud:4566/${req.file.key}` } else { - // @ts-expect-error property `location` doesn't exists in @types/express url = req.file.location } const result = await user.uploadPicture(userId, url) - if (result instanceof Error) { - badRequest(res, result.message); return - } - - res.json(result) + handleResponse(res, result) } export default userUploadPictureController diff --git a/src/lib/compress-image.ts b/src/helpers/compress-image.ts similarity index 100% rename from src/lib/compress-image.ts rename to src/helpers/compress-image.ts diff --git a/src/helpers/handle-response.ts b/src/helpers/handle-response.ts new file mode 100644 index 0000000..8afebb9 --- /dev/null +++ b/src/helpers/handle-response.ts @@ -0,0 +1,10 @@ +import { type Response } from 'express' +import { badRequest } from './http-errors' + +export default function handleResponse (res: Response, result: any): void { + if (result instanceof Error) { + badRequest(res, result.message) + } else { + res.json(result) + } +} diff --git a/src/lib/http-errors.ts b/src/helpers/http-errors.ts similarity index 100% rename from src/lib/http-errors.ts rename to src/helpers/http-errors.ts diff --git a/src/helpers/logger.ts b/src/helpers/logger.ts new file mode 100644 index 0000000..26237fc --- /dev/null +++ b/src/helpers/logger.ts @@ -0,0 +1,52 @@ +import winston from 'winston' + +const levels = { + error: 0, + warn: 1, + info: 2, + http: 3, + debug: 4 +} + +const level = (): string => { + // eslint-disable-next-line @typescript-eslint/strict-boolean-expressions, @typescript-eslint/prefer-nullish-coalescing + const env = process.env.NODE_ENV || 'development' + const isDevelopment = env === 'development' + return isDevelopment ? 'debug' : 'warn' +} + +const colors = { + error: 'red', + warn: 'yellow', + info: 'green', + http: 'magenta', + debug: 'white' +} + +winston.addColors(colors) + +const format = winston.format.combine( + winston.format.timestamp({ format: 'YYYY-MM-DD HH:mm:ss:ms' }), + winston.format.colorize({ all: true }), + winston.format.printf( + // eslint-disable-next-line @typescript-eslint/restrict-template-expressions + (info) => `${info.timestamp} ${info.level}: ${info.message}` + ) +) + +const transports = [ + new winston.transports.Console(), + new winston.transports.File({ + filename: 'logs/error.log', + level: 'error' + }) +] + +const logger = winston.createLogger({ + level: level(), + levels, + format, + transports +}) + +export default logger diff --git a/src/helpers/notification.ts b/src/helpers/notification.ts new file mode 100644 index 0000000..e6d6964 --- /dev/null +++ b/src/helpers/notification.ts @@ -0,0 +1,47 @@ +import { type NotificationType } from '@prisma/client' +import prisma from 'clients/prisma-client' + +export async function createNotification ( + fromUserId: string, + toUserId: string, + content: string, + type: NotificationType +): Promise | Error> { + try { + await prisma.notifications.create({ + data: { + type, + fromUserId, + toUserId, + content + }, + include: { + fromUser: { + select: { + id: true, + displayName: true, + username: true, + profileImage: true + } + } + } + }) + return {} + } catch (_) { + return new Error('Error while creating notification') + } +} + +export async function countNotifications (toUserId: string): Promise { + try { + const count = await prisma.notifications.count({ + where: { + toUserId + } + }) + + return count + } catch (_) { + return new Error('Error while counting user notifications') + } +} diff --git a/src/interfaces/user.ts b/src/interfaces/user.ts index 8b2598a..f22dbac 100644 --- a/src/interfaces/user.ts +++ b/src/interfaces/user.ts @@ -1,10 +1,13 @@ -interface userPayload { +interface User { id?: string displayName?: string | null username?: string email?: string password?: string + profileImage?: string | null + createdAt?: Date token?: string + socketId?: string } -export default userPayload +export default User diff --git a/src/lib/create-notification.ts b/src/lib/create-notification.ts deleted file mode 100644 index 61aca7a..0000000 --- a/src/lib/create-notification.ts +++ /dev/null @@ -1,29 +0,0 @@ -import { type NotificationType } from '@prisma/client' -import prisma from 'clients/prisma-client' - -export default async function createNotification ( - fromUserId: string, - toUserId: string, - content: string, - type: NotificationType -): Promise | Error> { - await prisma.notifications.create({ - data: { - type, - fromUserId, - toUserId, - content - }, - include: { - fromUser: { - select: { - id: true, - displayName: true, - username: true, - profileImage: true - } - } - } - }) - return {} -} diff --git a/src/middlewares/authenticated.ts b/src/middlewares/authenticated.ts index 87cdd71..7e551cb 100644 --- a/src/middlewares/authenticated.ts +++ b/src/middlewares/authenticated.ts @@ -2,7 +2,7 @@ import { verify } from 'jsonwebtoken' import prisma from 'clients/prisma-client' import type { Response, Request, NextFunction } from 'express' import type jwtPayload from 'interfaces/jwt' -import { unauthorized } from 'lib/http-errors' +import { unauthorized } from 'helpers/http-errors' async function authenticated ( req: Request, diff --git a/src/middlewares/morgan.ts b/src/middlewares/morgan.ts new file mode 100644 index 0000000..deba3f8 --- /dev/null +++ b/src/middlewares/morgan.ts @@ -0,0 +1,13 @@ +import morgan, { type StreamOptions } from 'morgan' +import logger from 'helpers/logger' + +const stream: StreamOptions = { + write: (message) => logger.http(message) +} + +const morganMiddleware = morgan( + ':method :url :status - :response-time ms', + { stream } +) + +export default morganMiddleware diff --git a/src/middlewares/rate-limit.ts b/src/middlewares/rate-limit.ts index 844bb85..edadcb8 100644 --- a/src/middlewares/rate-limit.ts +++ b/src/middlewares/rate-limit.ts @@ -1,11 +1,12 @@ import rateLimit from 'express-rate-limit' import RedisStore from 'rate-limit-redis' import redis from 'clients/redis-client' +import logger from 'helpers/logger' let maxConnections if (process.env.NODE_ENV === 'development') { - console.log('Detected Development environment, disabling rate limiter.') + logger.info('Development environment detected. Rate limit is now disabled.') maxConnections = 0 } else { maxConnections = 5 diff --git a/src/middlewares/upload-image.ts b/src/middlewares/upload-image.ts index 0a72ac6..9ca543a 100644 --- a/src/middlewares/upload-image.ts +++ b/src/middlewares/upload-image.ts @@ -1,10 +1,10 @@ import type { Response, Request, NextFunction } from 'express' import multer from 'multer' import multerConfig from 'config/multer' -import compressImage from 'lib/compress-image' -import { badRequest } from 'lib/http-errors' +import compressImage from 'helpers/compress-image' +import { badRequest } from 'helpers/http-errors' -function uploadImage (req: Request, res: Response, next: NextFunction) { +function uploadImage (req: Request, res: Response, next: NextFunction): void { const upload = multer(multerConfig).single('image') upload(req, res, async (cb: multer.MulterError | Error | any) => { @@ -23,7 +23,6 @@ function uploadImage (req: Request, res: Response, next: NextFunction) { badRequest(res, 'Expected file'); return } - // @ts-expect-error property `key` does not exists in types await compressImage(req.file?.key, req.body.isProfilePicture) next() diff --git a/src/routes.ts b/src/routes.ts index bd0a48f..4a4f5c6 100644 --- a/src/routes.ts +++ b/src/routes.ts @@ -1,9 +1,9 @@ import { Router } from 'express' // Routers -import usersRouter from './controllers/users-router' -import postsRouter from './controllers/posts-router' -import commentsRouter from './controllers/comments-router' +import usersRouter from 'controllers/users-router' +import postsRouter from 'controllers/posts-router' +import commentsRouter from 'controllers/comments-router' const router = Router() diff --git a/src/server.ts b/src/server.ts index 4d209cb..2e6d3bb 100644 --- a/src/server.ts +++ b/src/server.ts @@ -1,5 +1,17 @@ import app from './app' +import { createServer } from 'http' +import logger from 'helpers/logger' +import createSocketIOInstance from 'socket' -app.listen(process.env.SERVER_PORT, () => { - console.log(`Server is running @ ${process.env.SERVER_PORT ?? ''}`) +const server = createServer(app) +const io = createSocketIOInstance(server) + +app.use((req, res, next) => { + // @ts-expect-error TODO: add io type + req.io = io + next() +}) + +server.listen(process.env.SERVER_PORT, () => { + logger.info(`Server is running @ ${process.env.SERVER_PORT ?? ''}`) }) diff --git a/src/services/users/auth.ts b/src/services/users/auth.ts index 671e183..08c1474 100644 --- a/src/services/users/auth.ts +++ b/src/services/users/auth.ts @@ -1,12 +1,12 @@ import * as bcrypt from 'bcrypt' import jsonwebtoken from 'jsonwebtoken' import prisma from 'clients/prisma-client' -import type userPayload from 'interfaces/user' +import type User from 'interfaces/user' async function userAuthService ({ email, password -}: userPayload): Promise | Error> { +}: User): Promise | Error> { const user = await prisma.user.findFirst({ where: { email diff --git a/src/services/users/signup.ts b/src/services/users/signup.ts index 5ac771f..c0112f9 100644 --- a/src/services/users/signup.ts +++ b/src/services/users/signup.ts @@ -1,7 +1,7 @@ import * as bcrypt from 'bcrypt' import validator from 'validator' import prisma from 'clients/prisma-client' -import type userPayload from 'interfaces/user' +import type User from 'interfaces/user' const passwordRegex = /^(?=.*[0-9])(?=.*[!@#$%^&*_])[a-zA-Z0-9!@#$%^&*_]{8,}$/ const usernameRegex = /^[a-zA-Z0-9_.]{5,15}$/ @@ -10,7 +10,7 @@ async function userSignupService ({ username, email, password -}: userPayload): Promise | Error> { +}: User): Promise | Error> { if (username === undefined || email === undefined || password === undefined) { return new Error('Missing fields') } diff --git a/src/services/users/update.ts b/src/services/users/update.ts index a3e6d7a..6a46546 100644 --- a/src/services/users/update.ts +++ b/src/services/users/update.ts @@ -1,4 +1,4 @@ -import type userPayload from 'interfaces/user' +import type User from 'interfaces/user' import prisma from 'clients/prisma-client' async function userUpdateService ({ @@ -6,7 +6,7 @@ async function userUpdateService ({ email, displayName, username -}: userPayload): Promise | Error> { +}: User): Promise | Error> { const user = await prisma.user.findFirst({ where: { id } }) if (user === null) { diff --git a/src/socket/index.ts b/src/socket/index.ts new file mode 100644 index 0000000..b2459b2 --- /dev/null +++ b/src/socket/index.ts @@ -0,0 +1,44 @@ +import prisma from 'clients/prisma-client' +import logger from 'helpers/logger' +import { Server } from 'socket.io' + +// eslint-disable-next-line @typescript-eslint/explicit-function-return-type +export default function createSocketIOInstance (httpServer: any) { + const io = new Server(httpServer, { + cors: { + origin: process.env.CORS_ORIGIN ?? '*' + } + }) + + io.use(async (socket, next) => { + // eslint-disable-next-line @typescript-eslint/strict-boolean-expressions + if (!socket.handshake.auth?.id) { + const error = new Error() + error.message = 'Unauthorized' + next(error) + } + + await prisma.user.update({ + where: { + id: socket.handshake.auth.id + }, + data: { + socketId: socket.id + } + }) + + next() + }) + + /* + const handleConnection = (socket) => { + // TODO + } + */ + + io.on('connection', (_) => { + logger.info('Placeholder') + }) + + return io +} diff --git a/src/tests/post/post-create.spec.ts b/src/tests/post/post-create.spec.ts index dff7569..882b238 100644 --- a/src/tests/post/post-create.spec.ts +++ b/src/tests/post/post-create.spec.ts @@ -2,9 +2,9 @@ import app from '../../app' import request from 'supertest' import signUpNewUser from '../utils/create-user' import deleteUser from '../utils/delete-user' -import type userPayload from '../../interfaces/user' +import type User from '../../interfaces/user' -let user: userPayload +let user: User describe('POST /post/create', () => { beforeAll(async () => { diff --git a/src/tests/post/post-delete.spec.ts b/src/tests/post/post-delete.spec.ts index b080a76..169e8cc 100644 --- a/src/tests/post/post-delete.spec.ts +++ b/src/tests/post/post-delete.spec.ts @@ -2,9 +2,9 @@ import app from '../../app' import request from 'supertest' import signUpNewUser from '../utils/create-user' import deleteUser from '../utils/delete-user' -import type userPayload from '../../interfaces/user' +import type User from '../../interfaces/user' -let user: userPayload +let user: User describe('DELETE /post/delete', () => { beforeAll(async () => { diff --git a/src/tests/post/post-info.spec.ts b/src/tests/post/post-info.spec.ts index c1f3094..df5fd65 100644 --- a/src/tests/post/post-info.spec.ts +++ b/src/tests/post/post-info.spec.ts @@ -2,11 +2,11 @@ import app from '../../app' import request from 'supertest' import signUpNewUser from '../utils/create-user' import deleteUser from '../utils/delete-user' -import type userPayload from '../../interfaces/user' +import type User from '../../interfaces/user' let postId: string -let user: userPayload +let user: User describe('POST /post/info', () => { beforeAll(async () => { diff --git a/src/tests/post/post-update.spec.ts b/src/tests/post/post-update.spec.ts index 963adee..8196304 100644 --- a/src/tests/post/post-update.spec.ts +++ b/src/tests/post/post-update.spec.ts @@ -2,9 +2,9 @@ import app from '../../app' import request from 'supertest' import signUpNewUser from '../utils/create-user' import deleteUser from '../utils/delete-user' -import type userPayload from '../../interfaces/user' +import type User from '../../interfaces/user' -let user: userPayload +let user: User describe('PUT /post/update', () => { beforeAll(async () => { diff --git a/src/tests/user/user-auth.spec.ts b/src/tests/user/user-auth.spec.ts index 20149a9..e601367 100644 --- a/src/tests/user/user-auth.spec.ts +++ b/src/tests/user/user-auth.spec.ts @@ -2,9 +2,9 @@ import request from 'supertest' import app from '../../app' import deleteUser from '../utils/delete-user' import signUpNewUser from '../utils/create-user' -import type userPayload from '../../interfaces/user' +import type User from '../../interfaces/user' -let user: userPayload +let user: User describe('POST /user/auth', () => { beforeAll(async () => { diff --git a/src/tests/user/user-delete.spec.ts b/src/tests/user/user-delete.spec.ts index cc72a52..a315fcb 100644 --- a/src/tests/user/user-delete.spec.ts +++ b/src/tests/user/user-delete.spec.ts @@ -1,9 +1,9 @@ import app from '../../app' import request from 'supertest' import signUpNewUser from '../utils/create-user' -import type userPayload from '../../interfaces/user' +import type User from '../../interfaces/user' -let user: userPayload +let user: User describe('DELETE /user/delete', () => { beforeAll(async () => { diff --git a/src/tests/user/user-info.spec.ts b/src/tests/user/user-info.spec.ts index 68f08e3..5ec3ddd 100644 --- a/src/tests/user/user-info.spec.ts +++ b/src/tests/user/user-info.spec.ts @@ -2,9 +2,9 @@ import app from '../../app' import request from 'supertest' import deleteUser from '../utils/delete-user' import signUpNewUser from '../utils/create-user' -import type userPayload from '../../interfaces/user' +import type User from '../../interfaces/user' -let user: userPayload +let user: User describe('POST /user/info', () => { beforeAll(async () => { diff --git a/src/tests/user/user-signup.spec.ts b/src/tests/user/user-signup.spec.ts index 2ce25c0..97d1c77 100644 --- a/src/tests/user/user-signup.spec.ts +++ b/src/tests/user/user-signup.spec.ts @@ -2,9 +2,9 @@ import request from 'supertest' import app from '../../app' import deleteUser from '../utils/delete-user' import signUpNewUser from '../utils/create-user' -import type userPayload from '../../interfaces/user' +import type User from '../../interfaces/user' -let user: userPayload +let user: User describe('POST /user/signup', () => { beforeAll(async () => { diff --git a/src/tests/user/user-update.spec.ts b/src/tests/user/user-update.spec.ts index 4e1dd99..5c6d0dc 100644 --- a/src/tests/user/user-update.spec.ts +++ b/src/tests/user/user-update.spec.ts @@ -2,9 +2,9 @@ import request from 'supertest' import app from '../../app' import signUpNewUser from '../utils/create-user' import deleteUser from '../utils/delete-user' -import type userPayload from '../../interfaces/user' +import type User from '../../interfaces/user' -let user: userPayload +let user: User describe('PUT /user/update', () => { beforeAll(async () => { diff --git a/src/tests/utils/create-user.ts b/src/tests/utils/create-user.ts index b06bdc7..0698037 100644 --- a/src/tests/utils/create-user.ts +++ b/src/tests/utils/create-user.ts @@ -1,9 +1,9 @@ import app from '../../app' import request from 'supertest' import { faker } from '@faker-js/faker' -import type userPayload from '../../interfaces/user' +import type User from '../../interfaces/user' -async function signUpNewUser (): Promise { +async function signUpNewUser (): Promise { // To avoid conflicts with existing usernames or emails const username = faker.internet.userName({ lastName: 'doe' }).toLowerCase() const email = faker.internet.email() diff --git a/tsconfig.json b/tsconfig.json index e1d0503..e3dbbbc 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -27,12 +27,11 @@ ], "services/*": [ "services/*" - ] + ], }, "typeRoots": [ - "src/@types/express.d.ts", - "src/@types/global.d.ts", - "node_modules/@types" + "./src/@types", + "./node_modules/@types" ], /* Emit */ "outDir": "./dist",