From 18d0cbdfb526341a2d9fec3aa2a1efa434185c31 Mon Sep 17 00:00:00 2001 From: NecroBread Date: Sun, 7 Jan 2024 18:24:04 +0200 Subject: [PATCH] replace mongo with postgresql --- package-lock.json | 759 +++++++++++++++++++-- package.json | 4 +- src/app.ts | 2 +- src/config/db.ts | 29 +- src/features/article/article-entity.ts | 24 + src/features/article/article-handlers.ts | 2 +- src/features/article/article-queries.ts | 51 -- src/features/article/article-repository.ts | 4 + src/features/article/article-schema.ts | 30 - src/features/article/article-service.ts | 58 +- src/features/article/article-types.ts | 32 +- src/features/article/article-validators.ts | 4 +- src/features/comment/comment-entity.ts | 29 + src/features/comment/comment-handlers.ts | 2 +- src/features/comment/comment-queries.ts | 52 -- src/features/comment/comment-repository.ts | 4 + src/features/comment/comment-schema.ts | 35 - src/features/comment/comment-service.ts | 57 +- src/features/comment/comment-types.ts | 35 +- src/features/comment/comment-validators.ts | 4 +- src/features/user/user-entity.ts | 35 + src/features/user/user-queries.ts | 46 -- src/features/user/user-repository.ts | 4 + src/features/user/user-schema.ts | 53 -- src/features/user/user-service.ts | 56 +- src/features/user/user-types.ts | 13 +- src/index.ts | 14 +- src/middleware/error-middleware.ts | 2 +- src/utils/build-query.ts | 73 ++ src/utils/parse-query.ts | 97 --- tsconfig.json | 4 +- 31 files changed, 1057 insertions(+), 557 deletions(-) create mode 100644 src/features/article/article-entity.ts delete mode 100644 src/features/article/article-queries.ts create mode 100644 src/features/article/article-repository.ts delete mode 100644 src/features/article/article-schema.ts create mode 100644 src/features/comment/comment-entity.ts delete mode 100644 src/features/comment/comment-queries.ts create mode 100644 src/features/comment/comment-repository.ts delete mode 100644 src/features/comment/comment-schema.ts create mode 100644 src/features/user/user-entity.ts delete mode 100644 src/features/user/user-queries.ts create mode 100644 src/features/user/user-repository.ts delete mode 100644 src/features/user/user-schema.ts create mode 100644 src/utils/build-query.ts delete mode 100644 src/utils/parse-query.ts diff --git a/package-lock.json b/package-lock.json index a7bd302..9000b0b 100644 --- a/package-lock.json +++ b/package-lock.json @@ -18,10 +18,12 @@ "helmet": "^7.1.0", "joi": "^17.11.0", "jsonwebtoken": "^9.0.2", - "mongoose": "^8.0.2", "mongoose-beautiful-unique-validation": "^7.1.1", "morgan": "^1.10.0", + "pg": "^8.11.3", + "reflect-metadata": "^0.2.1", "swagger-ui-express": "^5.0.0", + "typeorm": "^0.3.19", "winston": "^3.11.0" }, "devDependencies": { @@ -751,7 +753,7 @@ "version": "0.8.1", "resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz", "integrity": "sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==", - "dev": true, + "devOptional": true, "dependencies": { "@jridgewell/trace-mapping": "0.3.9" }, @@ -763,7 +765,7 @@ "version": "0.3.9", "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz", "integrity": "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==", - "dev": true, + "devOptional": true, "dependencies": { "@jridgewell/resolve-uri": "^3.0.3", "@jridgewell/sourcemap-codec": "^1.4.10" @@ -1420,7 +1422,7 @@ "version": "3.1.1", "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.1.tgz", "integrity": "sha512-dSYZh7HhCDtCKm4QakX0xFpsRDqjjtZf/kjI/v3T3Nwt5r8/qz/M19F9ySyOqU94SXBmeG9ttTul+YnR4LOxFA==", - "dev": true, + "devOptional": true, "engines": { "node": ">=6.0.0" } @@ -1438,7 +1440,7 @@ "version": "1.4.15", "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz", "integrity": "sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==", - "dev": true + "devOptional": true }, "node_modules/@jridgewell/trace-mapping": { "version": "0.3.20", @@ -1501,6 +1503,7 @@ "version": "1.1.1", "resolved": "https://registry.npmjs.org/@mongodb-js/saslprep/-/saslprep-1.1.1.tgz", "integrity": "sha512-t7c5K033joZZMspnHg/gWPE4kandgc2OxE74aYOtGKfgB9VPuVJPix0H6fhmm2erj5PBJ21mqcx34lpIGtUCsQ==", + "peer": true, "dependencies": { "sparse-bitfield": "^3.0.3" } @@ -1591,29 +1594,34 @@ "@sinonjs/commons": "^3.0.0" } }, + "node_modules/@sqltools/formatter": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/@sqltools/formatter/-/formatter-1.2.5.tgz", + "integrity": "sha512-Uy0+khmZqUrUGm5dmMqVlnvufZRSK0FbYzVgp0UMstm+F5+W2/jnEEQyc9vo1ZR/E5ZI/B1WjjoTqBqwJL6Krw==" + }, "node_modules/@tsconfig/node10": { "version": "1.0.9", "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.9.tgz", "integrity": "sha512-jNsYVVxU8v5g43Erja32laIDHXeoNvFEpX33OK4d6hljo3jDhCBDhx5dhCCTMWUojscpAagGiRkBKxpdl9fxqA==", - "dev": true + "devOptional": true }, "node_modules/@tsconfig/node12": { "version": "1.0.11", "resolved": "https://registry.npmjs.org/@tsconfig/node12/-/node12-1.0.11.tgz", "integrity": "sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==", - "dev": true + "devOptional": true }, "node_modules/@tsconfig/node14": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.3.tgz", "integrity": "sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==", - "dev": true + "devOptional": true }, "node_modules/@tsconfig/node16": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.4.tgz", "integrity": "sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA==", - "dev": true + "devOptional": true }, "node_modules/@types/babel__core": { "version": "7.20.5", @@ -1913,12 +1921,14 @@ "node_modules/@types/webidl-conversions": { "version": "7.0.3", "resolved": "https://registry.npmjs.org/@types/webidl-conversions/-/webidl-conversions-7.0.3.tgz", - "integrity": "sha512-CiJJvcRtIgzadHCYXw7dqEnMNRjhGZlYK05Mj9OyktqV8uVT8fD2BFOB7S1uwBE3Kj2Z+4UyPmFw/Ixgw/LAlA==" + "integrity": "sha512-CiJJvcRtIgzadHCYXw7dqEnMNRjhGZlYK05Mj9OyktqV8uVT8fD2BFOB7S1uwBE3Kj2Z+4UyPmFw/Ixgw/LAlA==", + "peer": true }, "node_modules/@types/whatwg-url": { "version": "8.2.2", "resolved": "https://registry.npmjs.org/@types/whatwg-url/-/whatwg-url-8.2.2.tgz", "integrity": "sha512-FtQu10RWgn3D9U4aazdwIE2yzphmTJREDqNdODHrbrZmmMqI0vMheC/6NE/J1Yveaj8H+ela+YwWTjq5PGmuhA==", + "peer": true, "dependencies": { "@types/node": "*", "@types/webidl-conversions": "*" @@ -2111,7 +2121,7 @@ "version": "8.11.2", "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.11.2.tgz", "integrity": "sha512-nc0Axzp/0FILLEVsm4fNwLCwMttvhEI263QtVPQcbpfZZ3ts0hLsZGOpE6czNlid7CJ9MlyH8reXkpsf3YUY4w==", - "dev": true, + "devOptional": true, "bin": { "acorn": "bin/acorn" }, @@ -2132,7 +2142,7 @@ "version": "8.3.1", "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.3.1.tgz", "integrity": "sha512-TgUZgYvqZprrl7YldZNoa9OciCAyZR+Ejm9eXzKCmjsF5IKp/wgQ7Z/ZpjpGTIUPwrHQIcYeI8qDh4PsEwxMbw==", - "dev": true, + "devOptional": true, "engines": { "node": ">=0.4.0" } @@ -2234,6 +2244,11 @@ "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, + "node_modules/any-promise": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/any-promise/-/any-promise-1.3.0.tgz", + "integrity": "sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A==" + }, "node_modules/anymatch": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", @@ -2247,6 +2262,14 @@ "node": ">= 8" } }, + "node_modules/app-root-path": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/app-root-path/-/app-root-path-3.1.0.tgz", + "integrity": "sha512-biN3PwB2gUtjaYy/isrU3aNWI5w+fAfvHkSvCKeQGxhmYpwKFUxudR3Yya+KqVRHBmEDYh+/lTozYCFbmzX4nA==", + "engines": { + "node": ">= 6.0.0" + } + }, "node_modules/aproba": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/aproba/-/aproba-2.0.0.tgz", @@ -2268,7 +2291,7 @@ "version": "4.1.3", "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz", "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==", - "dev": true + "devOptional": true }, "node_modules/argparse": { "version": "2.0.1", @@ -2428,6 +2451,25 @@ "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==" }, + "node_modules/base64-js": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", + "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, "node_modules/basic-auth": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/basic-auth/-/basic-auth-2.0.1.tgz", @@ -2567,10 +2609,34 @@ "version": "6.2.0", "resolved": "https://registry.npmjs.org/bson/-/bson-6.2.0.tgz", "integrity": "sha512-ID1cI+7bazPDyL9wYy9GaQ8gEEohWvcUl/Yf0dIdutJxnmInEEyCsb4awy/OiBfall7zBA179Pahi3vCdFze3Q==", + "peer": true, "engines": { "node": ">=16.20.1" } }, + "node_modules/buffer": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz", + "integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "dependencies": { + "base64-js": "^1.3.1", + "ieee754": "^1.2.1" + } + }, "node_modules/buffer-equal-constant-time": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz", @@ -2582,6 +2648,14 @@ "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", "dev": true }, + "node_modules/buffer-writer": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/buffer-writer/-/buffer-writer-2.0.0.tgz", + "integrity": "sha512-a7ZpuTZU1TRtnwyCNW3I5dc0wWNC3VR9S++Ewyk2HHZdrO3CQJqSpd+95Us590V6AL7JqUAH2IwZ/398PmNFgw==", + "engines": { + "node": ">=4" + } + }, "node_modules/bytes": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", @@ -2645,7 +2719,6 @@ "version": "4.1.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" @@ -2661,7 +2734,6 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true, "engines": { "node": ">=8" } @@ -2670,7 +2742,6 @@ "version": "7.2.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, "dependencies": { "has-flag": "^4.0.0" }, @@ -2743,11 +2814,65 @@ "integrity": "sha512-0TNiGstbQmCFwt4akjjBg5pLRTSyj/PkWQ1ZoO2zntmg9yLqSRxwEa4iCfQLGjqhiqBfOJa7W/E8wfGrTDmlZQ==", "dev": true }, + "node_modules/cli-highlight": { + "version": "2.1.11", + "resolved": "https://registry.npmjs.org/cli-highlight/-/cli-highlight-2.1.11.tgz", + "integrity": "sha512-9KDcoEVwyUXrjcJNvHD0NFc/hiwe/WPVYIleQh2O1N2Zro5gWJZ/K+3DGn8w8P/F6FxOgzyC5bxDyHIgCSPhGg==", + "dependencies": { + "chalk": "^4.0.0", + "highlight.js": "^10.7.1", + "mz": "^2.4.0", + "parse5": "^5.1.1", + "parse5-htmlparser2-tree-adapter": "^6.0.0", + "yargs": "^16.0.0" + }, + "bin": { + "highlight": "bin/highlight" + }, + "engines": { + "node": ">=8.0.0", + "npm": ">=5.0.0" + } + }, + "node_modules/cli-highlight/node_modules/cliui": { + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", + "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.0", + "wrap-ansi": "^7.0.0" + } + }, + "node_modules/cli-highlight/node_modules/yargs": { + "version": "16.2.0", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", + "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==", + "dependencies": { + "cliui": "^7.0.2", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.0", + "y18n": "^5.0.5", + "yargs-parser": "^20.2.2" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/cli-highlight/node_modules/yargs-parser": { + "version": "20.2.9", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.9.tgz", + "integrity": "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==", + "engines": { + "node": ">=10" + } + }, "node_modules/cliui": { "version": "8.0.1", "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", - "dev": true, "dependencies": { "string-width": "^4.2.0", "strip-ansi": "^6.0.1", @@ -2969,7 +3094,7 @@ "version": "1.1.1", "resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz", "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==", - "dev": true + "devOptional": true }, "node_modules/cross-spawn": { "version": "7.0.3", @@ -2984,6 +3109,11 @@ "node": ">= 8" } }, + "node_modules/dayjs": { + "version": "1.11.10", + "resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.11.10.tgz", + "integrity": "sha512-vjAczensTgRcqDERK0SR2XMwsF/tSvnvlv6VcF2GIhg6Sx4yOIt/irsr1RDJsKiIyBzJDpCoXiWWq28MqH2cnQ==" + }, "node_modules/debug": { "version": "2.6.9", "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", @@ -3096,7 +3226,7 @@ "version": "4.0.2", "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", - "dev": true, + "devOptional": true, "engines": { "node": ">=0.3.1" } @@ -3221,7 +3351,6 @@ "version": "3.1.1", "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==", - "dev": true, "engines": { "node": ">=6" } @@ -3923,7 +4052,6 @@ "version": "2.0.5", "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", - "dev": true, "engines": { "node": "6.* || 8.* || >= 10.*" } @@ -4146,6 +4274,14 @@ "node": ">=8" } }, + "node_modules/highlight.js": { + "version": "10.7.3", + "resolved": "https://registry.npmjs.org/highlight.js/-/highlight.js-10.7.3.tgz", + "integrity": "sha512-tzcUFauisWKNHaRkN4Wjl/ZA07gENAjFl3J/c480dprkGTg5EQstgaNFqBfUqCq54kZRIEcreTsAgF/m2quD7A==", + "engines": { + "node": "*" + } + }, "node_modules/html-escaper": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz", @@ -4220,6 +4356,25 @@ "node": ">=0.10.0" } }, + "node_modules/ieee754": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", + "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, "node_modules/ignore": { "version": "5.3.0", "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.0.tgz", @@ -4293,6 +4448,13 @@ "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" }, + "node_modules/ip": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ip/-/ip-2.0.0.tgz", + "integrity": "sha512-WKa+XuLG1A1R0UWhl2+1XQSi+fZWMsYKffMZTTYsiZaUD8k2yDAj5atimTUD2TZkyCkNEeYE5NhFZmupOGtjYQ==", + "optional": true, + "peer": true + }, "node_modules/ipaddr.js": { "version": "1.9.1", "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", @@ -5236,6 +5398,7 @@ "version": "2.5.1", "resolved": "https://registry.npmjs.org/kareem/-/kareem-2.5.1.tgz", "integrity": "sha512-7jFxRVm+jD+rkq3kY0iZDJfsO2/t4BBPeEb2qKn2lR/9KhuksYk5hxzfRYWMPV8P/x2d0kHD306YyWLzjjH+uA==", + "peer": true, "engines": { "node": ">=12.0.0" } @@ -5411,7 +5574,7 @@ "version": "1.3.6", "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==", - "dev": true + "devOptional": true }, "node_modules/makeerror": { "version": "1.0.12", @@ -5433,7 +5596,8 @@ "node_modules/memory-pager": { "version": "1.5.0", "resolved": "https://registry.npmjs.org/memory-pager/-/memory-pager-1.5.0.tgz", - "integrity": "sha512-ZS4Bp4r/Zoeq6+NLJpP+0Zzm0pR8whtGPf1XExKLJBAczGMnSi3It14OiNCStjQjM6NU1okjQGSxgEZN8eBYKg==" + "integrity": "sha512-ZS4Bp4r/Zoeq6+NLJpP+0Zzm0pR8whtGPf1XExKLJBAczGMnSi3It14OiNCStjQjM6NU1okjQGSxgEZN8eBYKg==", + "peer": true }, "node_modules/merge-descriptors": { "version": "1.0.1", @@ -5577,25 +5741,28 @@ } }, "node_modules/mongodb": { - "version": "6.2.0", - "resolved": "https://registry.npmjs.org/mongodb/-/mongodb-6.2.0.tgz", - "integrity": "sha512-d7OSuGjGWDZ5usZPqfvb36laQ9CPhnWkAGHT61x5P95p/8nMVeH8asloMwW6GcYFeB0Vj4CB/1wOTDG2RA9BFA==", + "version": "5.9.2", + "resolved": "https://registry.npmjs.org/mongodb/-/mongodb-5.9.2.tgz", + "integrity": "sha512-H60HecKO4Bc+7dhOv4sJlgvenK4fQNqqUIlXxZYQNbfEWSALGAwGoyJd/0Qwk4TttFXUOHJ2ZJQe/52ScaUwtQ==", + "optional": true, + "peer": true, "dependencies": { - "@mongodb-js/saslprep": "^1.1.0", - "bson": "^6.2.0", - "mongodb-connection-string-url": "^2.6.0" + "bson": "^5.5.0", + "mongodb-connection-string-url": "^2.6.0", + "socks": "^2.7.1" }, "engines": { - "node": ">=16.20.1" + "node": ">=14.20.1" + }, + "optionalDependencies": { + "@mongodb-js/saslprep": "^1.1.0" }, "peerDependencies": { "@aws-sdk/credential-providers": "^3.188.0", - "@mongodb-js/zstd": "^1.1.0", - "gcp-metadata": "^5.2.0", - "kerberos": "^2.0.1", - "mongodb-client-encryption": ">=6.0.0 <7", - "snappy": "^7.2.2", - "socks": "^2.7.1" + "@mongodb-js/zstd": "^1.0.0", + "kerberos": "^1.0.0 || ^2.0.0", + "mongodb-client-encryption": ">=2.3.0 <3", + "snappy": "^7.2.2" }, "peerDependenciesMeta": { "@aws-sdk/credential-providers": { @@ -5604,9 +5771,6 @@ "@mongodb-js/zstd": { "optional": true }, - "gcp-metadata": { - "optional": true - }, "kerberos": { "optional": true }, @@ -5615,9 +5779,6 @@ }, "snappy": { "optional": true - }, - "socks": { - "optional": true } } }, @@ -5625,15 +5786,27 @@ "version": "2.6.0", "resolved": "https://registry.npmjs.org/mongodb-connection-string-url/-/mongodb-connection-string-url-2.6.0.tgz", "integrity": "sha512-WvTZlI9ab0QYtTYnuMLgobULWhokRjtC7db9LtcVfJ+Hsnyr5eo6ZtNAt3Ly24XZScGMelOcGtm7lSn0332tPQ==", + "peer": true, "dependencies": { "@types/whatwg-url": "^8.2.1", "whatwg-url": "^11.0.0" } }, + "node_modules/mongodb/node_modules/bson": { + "version": "5.5.1", + "resolved": "https://registry.npmjs.org/bson/-/bson-5.5.1.tgz", + "integrity": "sha512-ix0EwukN2EpC0SRWIj/7B5+A6uQMQy6KMREI9qQqvgpkV2frH63T0UDVd1SYedL6dNCmDBYB3QtXi4ISk9YT+g==", + "optional": true, + "peer": true, + "engines": { + "node": ">=14.20.1" + } + }, "node_modules/mongoose": { "version": "8.0.2", "resolved": "https://registry.npmjs.org/mongoose/-/mongoose-8.0.2.tgz", "integrity": "sha512-Vsi9GzTXjdBVzheT1HZOZ2jHNzzR9Xwb5OyLz/FvDEAhlwrRnXnuqJf0QHINUOQSm7aoyvnPks0q85HJkd6yDw==", + "peer": true, "dependencies": { "bson": "^6.2.0", "kareem": "2.5.1", @@ -5662,10 +5835,57 @@ "mongoose": ">=4.11.4" } }, + "node_modules/mongoose/node_modules/mongodb": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/mongodb/-/mongodb-6.2.0.tgz", + "integrity": "sha512-d7OSuGjGWDZ5usZPqfvb36laQ9CPhnWkAGHT61x5P95p/8nMVeH8asloMwW6GcYFeB0Vj4CB/1wOTDG2RA9BFA==", + "peer": true, + "dependencies": { + "@mongodb-js/saslprep": "^1.1.0", + "bson": "^6.2.0", + "mongodb-connection-string-url": "^2.6.0" + }, + "engines": { + "node": ">=16.20.1" + }, + "peerDependencies": { + "@aws-sdk/credential-providers": "^3.188.0", + "@mongodb-js/zstd": "^1.1.0", + "gcp-metadata": "^5.2.0", + "kerberos": "^2.0.1", + "mongodb-client-encryption": ">=6.0.0 <7", + "snappy": "^7.2.2", + "socks": "^2.7.1" + }, + "peerDependenciesMeta": { + "@aws-sdk/credential-providers": { + "optional": true + }, + "@mongodb-js/zstd": { + "optional": true + }, + "gcp-metadata": { + "optional": true + }, + "kerberos": { + "optional": true + }, + "mongodb-client-encryption": { + "optional": true + }, + "snappy": { + "optional": true + }, + "socks": { + "optional": true + } + } + }, "node_modules/mongoose/node_modules/ms": { "version": "2.1.3", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "peer": true }, "node_modules/morgan": { "version": "1.10.0", @@ -5697,6 +5917,7 @@ "version": "0.9.0", "resolved": "https://registry.npmjs.org/mpath/-/mpath-0.9.0.tgz", "integrity": "sha512-ikJRQTk8hw5DEoFVxHG1Gn9T/xcjtdnOKIU1JTmGjZZlg9LST2mBLmcX3/ICIbgJydT2GOc15RnNy5mHmzfSew==", + "peer": true, "engines": { "node": ">=4.0.0" } @@ -5705,6 +5926,7 @@ "version": "5.0.0", "resolved": "https://registry.npmjs.org/mquery/-/mquery-5.0.0.tgz", "integrity": "sha512-iQMncpmEK8R8ncT8HJGsGc9Dsp8xcgYMVSbs5jgnm1lFHTZqMJTUWTDx1LBO8+mK3tPNZWFLBghQEIOULSTHZg==", + "peer": true, "dependencies": { "debug": "4.x" }, @@ -5716,6 +5938,7 @@ "version": "4.3.4", "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "peer": true, "dependencies": { "ms": "2.1.2" }, @@ -5731,13 +5954,24 @@ "node_modules/mquery/node_modules/ms": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "peer": true }, "node_modules/ms": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" }, + "node_modules/mz": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/mz/-/mz-2.7.0.tgz", + "integrity": "sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==", + "dependencies": { + "any-promise": "^1.0.0", + "object-assign": "^4.0.1", + "thenify-all": "^1.0.0" + } + }, "node_modules/natural-compare": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", @@ -6032,6 +6266,11 @@ "node": ">=6" } }, + "node_modules/packet-reader": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/packet-reader/-/packet-reader-1.0.0.tgz", + "integrity": "sha512-HAKu/fG3HpHFO0AA8WE8q2g+gBJaZ9MG7fcKk+IJPLTGAD6Psw4443l+9DGRbOIh3/aXr7Phy0TjilYivJo5XQ==" + }, "node_modules/parent-module": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", @@ -6062,6 +6301,24 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/parse5": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/parse5/-/parse5-5.1.1.tgz", + "integrity": "sha512-ugq4DFI0Ptb+WWjAdOK16+u/nHfiIrcE+sh8kZMaM0WllQKLI9rOUq6c2b7cwPkXdzfQESqvoqK6ug7U/Yyzug==" + }, + "node_modules/parse5-htmlparser2-tree-adapter": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/parse5-htmlparser2-tree-adapter/-/parse5-htmlparser2-tree-adapter-6.0.1.tgz", + "integrity": "sha512-qPuWvbLgvDGilKc5BoicRovlT4MtYT6JfJyBOMDsKoiT+GiuP5qyrPCnR9HcPECIJJmZh5jRndyNThnhhb/vlA==", + "dependencies": { + "parse5": "^6.0.1" + } + }, + "node_modules/parse5-htmlparser2-tree-adapter/node_modules/parse5": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/parse5/-/parse5-6.0.1.tgz", + "integrity": "sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw==" + }, "node_modules/parseurl": { "version": "1.3.3", "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", @@ -6138,6 +6395,89 @@ "node": ">=8" } }, + "node_modules/pg": { + "version": "8.11.3", + "resolved": "https://registry.npmjs.org/pg/-/pg-8.11.3.tgz", + "integrity": "sha512-+9iuvG8QfaaUrrph+kpF24cXkH1YOOUeArRNYIxq1viYHZagBxrTno7cecY1Fa44tJeZvaoG+Djpkc3JwehN5g==", + "dependencies": { + "buffer-writer": "2.0.0", + "packet-reader": "1.0.0", + "pg-connection-string": "^2.6.2", + "pg-pool": "^3.6.1", + "pg-protocol": "^1.6.0", + "pg-types": "^2.1.0", + "pgpass": "1.x" + }, + "engines": { + "node": ">= 8.0.0" + }, + "optionalDependencies": { + "pg-cloudflare": "^1.1.1" + }, + "peerDependencies": { + "pg-native": ">=3.0.1" + }, + "peerDependenciesMeta": { + "pg-native": { + "optional": true + } + } + }, + "node_modules/pg-cloudflare": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/pg-cloudflare/-/pg-cloudflare-1.1.1.tgz", + "integrity": "sha512-xWPagP/4B6BgFO+EKz3JONXv3YDgvkbVrGw2mTo3D6tVDQRh1e7cqVGvyR3BE+eQgAvx1XhW/iEASj4/jCWl3Q==", + "optional": true + }, + "node_modules/pg-connection-string": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/pg-connection-string/-/pg-connection-string-2.6.2.tgz", + "integrity": "sha512-ch6OwaeaPYcova4kKZ15sbJ2hKb/VP48ZD2gE7i1J+L4MspCtBMAx8nMgz7bksc7IojCIIWuEhHibSMFH8m8oA==" + }, + "node_modules/pg-int8": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/pg-int8/-/pg-int8-1.0.1.tgz", + "integrity": "sha512-WCtabS6t3c8SkpDBUlb1kjOs7l66xsGdKpIPZsg4wR+B3+u9UAum2odSsF9tnvxg80h4ZxLWMy4pRjOsFIqQpw==", + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/pg-pool": { + "version": "3.6.1", + "resolved": "https://registry.npmjs.org/pg-pool/-/pg-pool-3.6.1.tgz", + "integrity": "sha512-jizsIzhkIitxCGfPRzJn1ZdcosIt3pz9Sh3V01fm1vZnbnCMgmGl5wvGGdNN2EL9Rmb0EcFoCkixH4Pu+sP9Og==", + "peerDependencies": { + "pg": ">=8.0" + } + }, + "node_modules/pg-protocol": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/pg-protocol/-/pg-protocol-1.6.0.tgz", + "integrity": "sha512-M+PDm637OY5WM307051+bsDia5Xej6d9IR4GwJse1qA1DIhiKlksvrneZOYQq42OM+spubpcNYEo2FcKQrDk+Q==" + }, + "node_modules/pg-types": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/pg-types/-/pg-types-2.2.0.tgz", + "integrity": "sha512-qTAAlrEsl8s4OiEQY69wDvcMIdQN6wdz5ojQiOy6YRMuynxenON0O5oCpJI6lshc6scgAY8qvJ2On/p+CXY0GA==", + "dependencies": { + "pg-int8": "1.0.1", + "postgres-array": "~2.0.0", + "postgres-bytea": "~1.0.0", + "postgres-date": "~1.0.4", + "postgres-interval": "^1.1.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/pgpass": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/pgpass/-/pgpass-1.0.5.tgz", + "integrity": "sha512-FdW9r/jQZhSeohs1Z3sI1yxFQNFvMcnmfuj4WBMUTxOrAyLMaTcE1aAMBiTlbMNaXvBCQuVi0R7hd8udDSP7ug==", + "dependencies": { + "split2": "^4.1.0" + } + }, "node_modules/picocolors": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", @@ -6229,6 +6569,41 @@ "node": ">=8" } }, + "node_modules/postgres-array": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/postgres-array/-/postgres-array-2.0.0.tgz", + "integrity": "sha512-VpZrUqU5A69eQyW2c5CA1jtLecCsN2U/bD6VilrFDWq5+5UIEVO7nazS3TEcHf1zuPYO/sqGvUvW62g86RXZuA==", + "engines": { + "node": ">=4" + } + }, + "node_modules/postgres-bytea": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/postgres-bytea/-/postgres-bytea-1.0.0.tgz", + "integrity": "sha512-xy3pmLuQqRBZBXDULy7KbaitYqLcmxigw14Q5sj8QBVLqEwXfeybIKVWiqAXTlcvdvb0+xkOtDbfQMOf4lST1w==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/postgres-date": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/postgres-date/-/postgres-date-1.0.7.tgz", + "integrity": "sha512-suDmjLVQg78nMK2UZ454hAG+OAW+HQPZ6n++TNDUX+L0+uUlLywnoxJKDou51Zm+zTCjrCl0Nq6J9C5hP9vK/Q==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/postgres-interval": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/postgres-interval/-/postgres-interval-1.2.0.tgz", + "integrity": "sha512-9ZhXKM/rw350N1ovuWHbGxnGh/SNJ4cnxHiM0rxE4VN41wsg8P8zWn9hv/buK00RP4WvlOyr/RBDiptyxVbkZQ==", + "dependencies": { + "xtend": "^4.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/prelude-ls": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", @@ -6406,11 +6781,15 @@ "node": ">=8.10.0" } }, + "node_modules/reflect-metadata": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/reflect-metadata/-/reflect-metadata-0.2.1.tgz", + "integrity": "sha512-i5lLI6iw9AU3Uu4szRNPPEkomnkjRTaVt9hy/bn5g/oSzekBSMeLZblcjP74AW0vBabqERLLIrz+gR8QYR54Tw==" + }, "node_modules/require-directory": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", - "dev": true, "engines": { "node": ">=0.10.0" } @@ -6680,6 +7059,18 @@ "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==" }, + "node_modules/sha.js": { + "version": "2.4.11", + "resolved": "https://registry.npmjs.org/sha.js/-/sha.js-2.4.11.tgz", + "integrity": "sha512-QMEp5B7cftE7APOjk5Y6xgrbWu+WkLVQwk8JNjZ8nKRciZaByEW6MubieAiToS7+dwvrjGhH8jRXz3MVd0AYqQ==", + "dependencies": { + "inherits": "^2.0.1", + "safe-buffer": "^5.0.1" + }, + "bin": { + "sha.js": "bin.js" + } + }, "node_modules/shebang-command": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", @@ -6715,7 +7106,8 @@ "node_modules/sift": { "version": "16.0.1", "resolved": "https://registry.npmjs.org/sift/-/sift-16.0.1.tgz", - "integrity": "sha512-Wv6BjQ5zbhW7VFefWusVP33T/EM0vYikCaQ2qR8yULbsilAT8/wQaXvuQ3ptGLpoKx+lihJE3y2UTgKDyyNHZQ==" + "integrity": "sha512-Wv6BjQ5zbhW7VFefWusVP33T/EM0vYikCaQ2qR8yULbsilAT8/wQaXvuQ3ptGLpoKx+lihJE3y2UTgKDyyNHZQ==", + "peer": true }, "node_modules/signal-exit": { "version": "3.0.7", @@ -6762,6 +7154,32 @@ "node": ">=8" } }, + "node_modules/smart-buffer": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/smart-buffer/-/smart-buffer-4.2.0.tgz", + "integrity": "sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg==", + "optional": true, + "peer": true, + "engines": { + "node": ">= 6.0.0", + "npm": ">= 3.0.0" + } + }, + "node_modules/socks": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/socks/-/socks-2.7.1.tgz", + "integrity": "sha512-7maUZy1N7uo6+WVEX6psASxtNlKaNVMlGQKkG/63nEDdLOWNbiUMoLK7X4uYoLhQstau72mLgfEWcXcwsaHbYQ==", + "optional": true, + "peer": true, + "dependencies": { + "ip": "^2.0.0", + "smart-buffer": "^4.2.0" + }, + "engines": { + "node": ">= 10.13.0", + "npm": ">= 3.0.0" + } + }, "node_modules/source-map": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", @@ -6784,10 +7202,19 @@ "version": "3.0.3", "resolved": "https://registry.npmjs.org/sparse-bitfield/-/sparse-bitfield-3.0.3.tgz", "integrity": "sha512-kvzhi7vqKTfkh0PZU+2D2PIllw2ymqJKujUcyPMd9Y75Nv4nPbGJZXNhxsgdQab2BmlDct1YnfQCguEvHr7VsQ==", + "peer": true, "dependencies": { "memory-pager": "^1.0.2" } }, + "node_modules/split2": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/split2/-/split2-4.2.0.tgz", + "integrity": "sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg==", + "engines": { + "node": ">= 10.x" + } + }, "node_modules/sprintf-js": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", @@ -7085,6 +7512,25 @@ "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==", "dev": true }, + "node_modules/thenify": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/thenify/-/thenify-3.3.1.tgz", + "integrity": "sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw==", + "dependencies": { + "any-promise": "^1.0.0" + } + }, + "node_modules/thenify-all": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/thenify-all/-/thenify-all-1.6.0.tgz", + "integrity": "sha512-RNxQH/qI8/t3thXJDwcstUO4zeqo64+Uy/+sNVRBx4Xn2OX+OZ9oP+iJnNFqplFra2ZUVeKCSa2oVWi3T4uVmA==", + "dependencies": { + "thenify": ">= 3.1.0 < 4" + }, + "engines": { + "node": ">=0.8" + } + }, "node_modules/tmpl": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/tmpl/-/tmpl-1.0.5.tgz", @@ -7136,6 +7582,7 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/tr46/-/tr46-3.0.0.tgz", "integrity": "sha512-l7FvfAHlcmulp8kr+flpQZmVwtu7nfRV7NZujtN0OqES8EL4O4e0qqzL0DC5gAvx/ZC/9lk6rhcUwYvkBnBnYA==", + "peer": true, "dependencies": { "punycode": "^2.1.1" }, @@ -7207,7 +7654,7 @@ "version": "10.9.1", "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.1.tgz", "integrity": "sha512-NtVysVPkxxrwFGUUxGYhfux8k78pQB3JqYBXlLRZgdGUqTO5wU/UyHop5p70iEbGhB7q5KmiZiU0Y3KlJrScEw==", - "dev": true, + "devOptional": true, "dependencies": { "@cspotcode/source-map-support": "^0.8.0", "@tsconfig/node10": "^1.0.7", @@ -7388,11 +7835,204 @@ "node": ">= 0.6" } }, + "node_modules/typeorm": { + "version": "0.3.19", + "resolved": "https://registry.npmjs.org/typeorm/-/typeorm-0.3.19.tgz", + "integrity": "sha512-OGelrY5qEoAU80mR1iyvmUHiKCPUydL6xp6bebXzS7jyv/X70Gp/jBWRAfF4qGOfy2A7orMiGRfwsBUNbEL65g==", + "dependencies": { + "@sqltools/formatter": "^1.2.5", + "app-root-path": "^3.1.0", + "buffer": "^6.0.3", + "chalk": "^4.1.2", + "cli-highlight": "^2.1.11", + "dayjs": "^1.11.9", + "debug": "^4.3.4", + "dotenv": "^16.0.3", + "glob": "^10.3.10", + "mkdirp": "^2.1.3", + "reflect-metadata": "^0.1.13", + "sha.js": "^2.4.11", + "tslib": "^2.5.0", + "uuid": "^9.0.0", + "yargs": "^17.6.2" + }, + "bin": { + "typeorm": "cli.js", + "typeorm-ts-node-commonjs": "cli-ts-node-commonjs.js", + "typeorm-ts-node-esm": "cli-ts-node-esm.js" + }, + "engines": { + "node": ">= 12.9.0" + }, + "funding": { + "url": "https://opencollective.com/typeorm" + }, + "peerDependencies": { + "@google-cloud/spanner": "^5.18.0", + "@sap/hana-client": "^2.12.25", + "better-sqlite3": "^7.1.2 || ^8.0.0 || ^9.0.0", + "hdb-pool": "^0.1.6", + "ioredis": "^5.0.4", + "mongodb": "^5.8.0", + "mssql": "^9.1.1 || ^10.0.1", + "mysql2": "^2.2.5 || ^3.0.1", + "oracledb": "^6.3.0", + "pg": "^8.5.1", + "pg-native": "^3.0.0", + "pg-query-stream": "^4.0.0", + "redis": "^3.1.1 || ^4.0.0", + "sql.js": "^1.4.0", + "sqlite3": "^5.0.3", + "ts-node": "^10.7.0", + "typeorm-aurora-data-api-driver": "^2.0.0" + }, + "peerDependenciesMeta": { + "@google-cloud/spanner": { + "optional": true + }, + "@sap/hana-client": { + "optional": true + }, + "better-sqlite3": { + "optional": true + }, + "hdb-pool": { + "optional": true + }, + "ioredis": { + "optional": true + }, + "mongodb": { + "optional": true + }, + "mssql": { + "optional": true + }, + "mysql2": { + "optional": true + }, + "oracledb": { + "optional": true + }, + "pg": { + "optional": true + }, + "pg-native": { + "optional": true + }, + "pg-query-stream": { + "optional": true + }, + "redis": { + "optional": true + }, + "sql.js": { + "optional": true + }, + "sqlite3": { + "optional": true + }, + "ts-node": { + "optional": true + }, + "typeorm-aurora-data-api-driver": { + "optional": true + } + } + }, + "node_modules/typeorm/node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/typeorm/node_modules/debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/typeorm/node_modules/glob": { + "version": "10.3.10", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.3.10.tgz", + "integrity": "sha512-fa46+tv1Ak0UPK1TOy/pZrIybNNt4HCv7SDzwyfiOZkvZLEbjsZkJBPtDHVshZjbecAoAGSC20MjLDG/qr679g==", + "dependencies": { + "foreground-child": "^3.1.0", + "jackspeak": "^2.3.5", + "minimatch": "^9.0.1", + "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0", + "path-scurry": "^1.10.1" + }, + "bin": { + "glob": "dist/esm/bin.mjs" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/typeorm/node_modules/minimatch": { + "version": "9.0.3", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.3.tgz", + "integrity": "sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg==", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/typeorm/node_modules/mkdirp": { + "version": "2.1.6", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-2.1.6.tgz", + "integrity": "sha512-+hEnITedc8LAtIP9u3HJDFIdcLV2vXP33sqLLIzkv1Db1zO/1OxbvYf0Y1OC/S/Qo5dxHXepofhmxL02PsKe+A==", + "bin": { + "mkdirp": "dist/cjs/src/bin.js" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/typeorm/node_modules/ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + }, + "node_modules/typeorm/node_modules/reflect-metadata": { + "version": "0.1.14", + "resolved": "https://registry.npmjs.org/reflect-metadata/-/reflect-metadata-0.1.14.tgz", + "integrity": "sha512-ZhYeb6nRaXCfhnndflDK8qI6ZQ/YcWZCISRAWICW9XYqMUwjZM9Z0DveWX/ABN01oxSHwVxKQmxeYZSsm0jh5A==" + }, + "node_modules/typeorm/node_modules/tslib": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==" + }, "node_modules/typescript": { "version": "5.3.3", "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.3.3.tgz", "integrity": "sha512-pXWcraxM0uxAS+tN0AG/BF2TyqmHO014Z070UsJ+pFvYuRSq8KH8DmWpnbXe0pEPDHXZV3FcAbJkijJ5oNEnWw==", - "dev": true, + "devOptional": true, "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" @@ -7484,11 +8124,23 @@ "node": ">= 0.4.0" } }, + "node_modules/uuid": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz", + "integrity": "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==", + "funding": [ + "https://github.com/sponsors/broofa", + "https://github.com/sponsors/ctavan" + ], + "bin": { + "uuid": "dist/bin/uuid" + } + }, "node_modules/v8-compile-cache-lib": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz", "integrity": "sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==", - "dev": true + "devOptional": true }, "node_modules/v8-to-istanbul": { "version": "9.2.0", @@ -7525,6 +8177,7 @@ "version": "7.0.0", "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-7.0.0.tgz", "integrity": "sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g==", + "peer": true, "engines": { "node": ">=12" } @@ -7533,6 +8186,7 @@ "version": "11.0.0", "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-11.0.0.tgz", "integrity": "sha512-RKT8HExMpoYx4igMiVMY83lN6UeITKJlBQ+vR/8ZJ8OCdSiN3RwCq+9gH0+Xzj0+5IrM6i4j/6LuvzbZIQgEcQ==", + "peer": true, "dependencies": { "tr46": "^3.0.0", "webidl-conversions": "^7.0.0" @@ -7606,7 +8260,6 @@ "version": "7.0.0", "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", - "dev": true, "dependencies": { "ansi-styles": "^4.0.0", "string-width": "^4.1.0", @@ -7658,7 +8311,6 @@ "version": "4.0.2", "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==", - "dev": true, "engines": { "node": ">=0.4" } @@ -7667,7 +8319,6 @@ "version": "5.0.8", "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", - "dev": true, "engines": { "node": ">=10" } @@ -7681,7 +8332,6 @@ "version": "17.7.2", "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", - "dev": true, "dependencies": { "cliui": "^8.0.1", "escalade": "^3.1.1", @@ -7699,7 +8349,6 @@ "version": "21.1.1", "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", - "dev": true, "engines": { "node": ">=12" } @@ -7708,7 +8357,7 @@ "version": "3.1.1", "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz", "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==", - "dev": true, + "devOptional": true, "engines": { "node": ">=6" } diff --git a/package.json b/package.json index 65cc28f..7d349ae 100644 --- a/package.json +++ b/package.json @@ -33,10 +33,12 @@ "helmet": "^7.1.0", "joi": "^17.11.0", "jsonwebtoken": "^9.0.2", - "mongoose": "^8.0.2", "mongoose-beautiful-unique-validation": "^7.1.1", "morgan": "^1.10.0", + "pg": "^8.11.3", + "reflect-metadata": "^0.2.1", "swagger-ui-express": "^5.0.0", + "typeorm": "^0.3.19", "winston": "^3.11.0" }, "devDependencies": { diff --git a/src/app.ts b/src/app.ts index 31c6b1d..72bbde3 100644 --- a/src/app.ts +++ b/src/app.ts @@ -1,7 +1,7 @@ import express from "express"; import cookieParser from "cookie-parser"; import cors from "cors"; -import router from "./root-router"; +import router from "./router"; import path from "path"; import handleErrors from "./middleware/error-middleware"; import loggerMiddleware from "./middleware/logger-middleware"; diff --git a/src/config/db.ts b/src/config/db.ts index 569d00e..a26ffda 100644 --- a/src/config/db.ts +++ b/src/config/db.ts @@ -1,14 +1,17 @@ -import mongoose from "mongoose"; +import { DataSource } from "typeorm"; +import "reflect-metadata"; +import { Article } from "../features/article/article-entity"; +import { Comment } from "../features/comment/comment-entity"; +import { User } from "../features/user/user-entity"; -export default async function db(dbConnectionString: string) { - try { - await mongoose.connect(dbConnectionString), - { - useNewUrlParser: true, - useUnifiedTopology: true, - }; - } catch (err) { - console.error("Error connecting to database"); - process.exit(1); - } -} +export const AppDataSource = new DataSource({ + type: "postgres", + host: "localhost", + port: 6543, + password: "123123", + username: "postgres", + database: "express_app", + synchronize: true, + logging: true, + entities: [Article, Comment, User], +}); diff --git a/src/features/article/article-entity.ts b/src/features/article/article-entity.ts new file mode 100644 index 0000000..6986c76 --- /dev/null +++ b/src/features/article/article-entity.ts @@ -0,0 +1,24 @@ +import { Column, Entity, JoinColumn, OneToOne, PrimaryGeneratedColumn } from "typeorm"; +import { User } from "../user/user-entity"; + +@Entity() +export class Article { + @PrimaryGeneratedColumn("uuid") + id: string; + + @Column({ type: "varchar", length: 300 }) + title: string; + + @Column({ type: "varchar", length: 2000 }) + content: string; + + @OneToOne(() => User) + @JoinColumn() + owner: string; + + @Column({ type: "date" }) + updated_at: Date; + + @Column({ type: "date" }) + created_at: Date; +} diff --git a/src/features/article/article-handlers.ts b/src/features/article/article-handlers.ts index 4d6976e..a42bce9 100644 --- a/src/features/article/article-handlers.ts +++ b/src/features/article/article-handlers.ts @@ -3,7 +3,7 @@ import * as articleService from "./article-service"; import { NextFunction, Request, Response } from "express"; import { BadRequestError, InternalServerError, NotFoundError } from "../../utils/app-error"; import { HttpStatusCode } from "../../utils/http-status-code"; -import { IFullQuery } from "../../utils/parse-query"; +import { IFullQuery } from "../../utils/build-query"; export function getAllArticles() { return async (req: Request<{}, {}, {}, IFullQuery>, res: Response, next: NextFunction) => { diff --git a/src/features/article/article-queries.ts b/src/features/article/article-queries.ts deleted file mode 100644 index 6f7c07b..0000000 --- a/src/features/article/article-queries.ts +++ /dev/null @@ -1,51 +0,0 @@ -import { IParsedQuery } from "../../utils/parse-query"; -import Article from "./article-schema"; -import { ICreateArticleData } from "./article-types"; - -export async function findArticlesByQuery(parsedQuery: IParsedQuery) { - const articles = await Article.find(parsedQuery.find) - .sort(parsedQuery.sort) - .limit(parsedQuery.pagination.limit) - .skip(parsedQuery.pagination.skip) - .populate(parsedQuery.populate, parsedQuery.limitPopulate) - .select(parsedQuery.select); - - if (!articles) { - return []; - } - - return articles; -} - -export async function countDocumentsByQuery(parsedQuery: IParsedQuery) { - if (parsedQuery.find) { - return Article.find(parsedQuery.find).countDocuments(); - } - - return Article.find().estimatedDocumentCount(); -} - -export async function findArticleById(id: string, populateField = { populate: "", limitPopulate: "" }) { - const article = await Article.findById(id).populate(populateField.populate, populateField.limitPopulate); - - if (!article) { - return null; - } - - return article; -} - -export async function createArticle(data: ICreateArticleData) { - const result = new Article(data); - await result.save(); - - return result; -} - -export async function deleteArticleById(id: string) { - await Article.findOneAndDelete({ _id: id }); -} - -export async function saveUpdatedArticle(article: { save: () => void }) { - return article.save(); -} diff --git a/src/features/article/article-repository.ts b/src/features/article/article-repository.ts new file mode 100644 index 0000000..9b464c3 --- /dev/null +++ b/src/features/article/article-repository.ts @@ -0,0 +1,4 @@ +import { AppDataSource } from "../../config/db"; +import { Article } from "./article-entity"; + +export const articleRepository = () => AppDataSource.getRepository(Article); diff --git a/src/features/article/article-schema.ts b/src/features/article/article-schema.ts deleted file mode 100644 index 608a8e7..0000000 --- a/src/features/article/article-schema.ts +++ /dev/null @@ -1,30 +0,0 @@ -import mongoose, { Schema, Types, model } from "mongoose"; -import { IArticle } from "./article-types"; -import beautifyUnique from "mongoose-beautiful-unique-validation"; - -mongoose.Schema.Types.String.set("trim", true); - -const articleSchema = new Schema( - { - title: { - type: String, - required: [true, `Title is required`], - }, - content: { - type: String, - required: [true, `Content is required`], - }, - owner: { - type: Types.ObjectId, - ref: "User", - required: [true, "Owner ID is required"], - }, - }, - { timestamps: true } -); - -articleSchema.plugin(beautifyUnique); - -const Article = model("Article", articleSchema); - -export default Article; diff --git a/src/features/article/article-service.ts b/src/features/article/article-service.ts index 1697f0c..44dabe2 100644 --- a/src/features/article/article-service.ts +++ b/src/features/article/article-service.ts @@ -1,33 +1,29 @@ import { NotFoundError, UnauthorizedError } from "../../utils/app-error"; -import { IFullQuery, parseQueryToMongoParams } from "../../utils/parse-query"; -import * as articleQueries from "./article-queries"; -import { ArticleDto, ICreateArticleData, IPatchArticleData } from "./article-types"; +import { IFullQuery, buildQuery } from "../../utils/build-query"; +import { Article } from "./article-entity"; +import { articleRepository } from "./article-repository"; +import { ArticleDto, ICreateArticleData, IPutArticleData } from "./article-types"; -export async function getAll(query: IFullQuery): Promise { - const parsedQuery = parseQueryToMongoParams(query); +export async function getAll(query: IFullQuery): Promise { + const articles = await buildQuery(articleRepository(), query); - if (parsedQuery.count) { - return articleQueries.countDocumentsByQuery(parsedQuery); + if (!articles) { + return []; } - const articles = await articleQueries.findArticlesByQuery(parsedQuery); - return articles.map((x) => new ArticleDto(x)); } -export async function getOne(id: string, query: any): Promise { - let populate = ""; - let limitPopulate = ""; +export async function getOne(id: string, query: any): Promise { + let queryBuilder = articleRepository().createQueryBuilder("entity"); - if (query && query.populate) { - populate += query.populate; + queryBuilder = queryBuilder.where("entity.id = :id", { id }); - if (query.populate.includes("owner")) { - limitPopulate += "username firstName lastName"; - } + if (query && query.populate) { + queryBuilder = queryBuilder.leftJoinAndSelect(`entity.${query.populate}`, query.populate); } - const article = await articleQueries.findArticleById(id, { populate, limitPopulate }); + const article = await queryBuilder.getOne(); if (!article) { throw new NotFoundError(); @@ -36,15 +32,23 @@ export async function getOne(id: string, query: any): Promise return new ArticleDto(article); } -export async function create(data: ICreateArticleData): Promise { - const article = await articleQueries.createArticle(data); +export async function create(data: ICreateArticleData & { owner: string }): Promise { + const article = new Article(); + article.content = data.content; + article.title = data.title; + article.owner = data.owner; + article.created_at = new Date(); + article.updated_at = new Date(); + + await articleRepository().save(article); + return new ArticleDto(article); } const ALLOWED_UPDATE_FIELDS = ["title", "content"]; -export async function update(id: string, userId: string, data: IPatchArticleData): Promise { - const article = await articleQueries.findArticleById(id); +export async function update(id: string, userId: string, data: IPutArticleData): Promise { + const article = await articleRepository().findOneBy({ id }); if (article === null) throw new NotFoundError(); if (article.owner.toString() !== userId) throw new UnauthorizedError(); @@ -55,16 +59,18 @@ export async function update(id: string, userId: string, data: IPatchArticleData } } - await articleQueries.saveUpdatedArticle(article); + article.updated_at = new Date(); + + await articleRepository().save(article); return new ArticleDto(article); } export async function remove(id: string, userId: string): Promise { - const article = await articleQueries.findArticleById(id); + const article = await articleRepository().findOneBy({ id }); if (!article) throw new NotFoundError(); - if (article.owner.toString() !== userId) throw new UnauthorizedError(); + if (article.owner !== userId) throw new UnauthorizedError(); - await articleQueries.deleteArticleById(id); + await articleRepository().remove(article); } diff --git a/src/features/article/article-types.ts b/src/features/article/article-types.ts index 4276c9d..e66c5c8 100644 --- a/src/features/article/article-types.ts +++ b/src/features/article/article-types.ts @@ -1,21 +1,17 @@ import { ObjectId } from "mongodb"; import { IUser, UserDto } from "../user/user-types"; import { isValidObjectId } from "mongoose"; +import { Article } from "./article-entity"; -export interface IArticle { - _id: ObjectId; +export type ICreateArticleData = { title: string; content: string; - owner: ObjectId | IUser; - createdAt: Date; - updatedAt: Date; - estimatedDocumentCount: () => number; - count: () => number; -} +}; -export type ICreateArticleData = Pick; - -export type IPatchArticleData = Pick; +export type IPutArticleData = { + title: string; + content: string; +}; /** * Represents a Public Article with limited information. @@ -28,17 +24,17 @@ export class ArticleDto { createdAt: Date; updatedAt: Date; - constructor(article: IArticle) { - this.id = article._id.toString(); + constructor(article: Article) { + this.id = article.id; this.title = article.title; this.content = article.content; - this.createdAt = article.createdAt; - this.updatedAt = article.updatedAt; + this.createdAt = article.created_at; + this.updatedAt = article.updated_at; - if (isValidObjectId(article.owner)) { - this.owner = (article.owner as ObjectId).toString(); + if (typeof article.owner === "string") { + this.owner = article.owner; } else { - this.owner = new UserDto(article.owner as IUser); + this.owner = new UserDto(article.owner); } } } diff --git a/src/features/article/article-validators.ts b/src/features/article/article-validators.ts index d100723..c2599d3 100644 --- a/src/features/article/article-validators.ts +++ b/src/features/article/article-validators.ts @@ -1,12 +1,12 @@ import Joi from "joi"; -import { ICreateArticleData, IPatchArticleData } from "./article-types"; +import { ICreateArticleData, IPutArticleData } from "./article-types"; export const articleCreateSchema = Joi.object().keys({ title: Joi.string().required().min(3).max(100), content: Joi.string().required().min(3).max(10000), }); -export const articlePatchSchema = Joi.object().keys({ +export const articlePatchSchema = Joi.object().keys({ title: Joi.string().optional().min(3).max(100), content: Joi.string().optional().min(3).max(10000), }); diff --git a/src/features/comment/comment-entity.ts b/src/features/comment/comment-entity.ts new file mode 100644 index 0000000..4b05d40 --- /dev/null +++ b/src/features/comment/comment-entity.ts @@ -0,0 +1,29 @@ +import { Column, Entity, JoinColumn, OneToOne, PrimaryGeneratedColumn } from "typeorm"; +import { User } from "../user/user-entity"; + +@Entity() +export class Comment { + @PrimaryGeneratedColumn("uuid") + id: string; + + @Column({ type: "varchar", length: 2000 }) + content: string; + + @OneToOne(() => User) + @JoinColumn() + owner: string; + + @OneToOne(() => Comment, { nullable: true }) + @JoinColumn() + parent: string; + + @OneToOne(() => User) + @JoinColumn() + article: string; + + @Column({ type: "date" }) + updated_at: Date; + + @Column({ type: "date" }) + created_at: Date; +} diff --git a/src/features/comment/comment-handlers.ts b/src/features/comment/comment-handlers.ts index ca8f13f..5b0be3d 100644 --- a/src/features/comment/comment-handlers.ts +++ b/src/features/comment/comment-handlers.ts @@ -3,7 +3,7 @@ import * as commentService from "./comment-service"; import { NextFunction, Request, Response } from "express"; import { BadRequestError, InternalServerError, NotFoundError } from "../../utils/app-error"; import { HttpStatusCode } from "../../utils/http-status-code"; -import { IFullQuery } from "../../utils/parse-query"; +import { IFullQuery } from "../../utils/build-query"; export function getAllComments() { return async (req: Request<{}, {}, {}, IFullQuery>, res: Response, next: NextFunction) => { diff --git a/src/features/comment/comment-queries.ts b/src/features/comment/comment-queries.ts deleted file mode 100644 index b57137a..0000000 --- a/src/features/comment/comment-queries.ts +++ /dev/null @@ -1,52 +0,0 @@ -import { Document, ObjectId } from "mongoose"; -import { IParsedQuery } from "../../utils/parse-query"; -import Comment from "./comment-schema"; -import { IComment, ICreateCommentData } from "./comment-types"; - -export async function findCommentsByQuery(parsedQuery: IParsedQuery) { - const comments = await Comment.find(parsedQuery.find) - .sort(parsedQuery.sort) - .limit(parsedQuery.pagination.limit) - .skip(parsedQuery.pagination.skip) - .populate(parsedQuery.populate, parsedQuery.limitPopulate) - .select(parsedQuery.select); - - if (!comments) { - return []; - } - - return comments; -} - -export async function countDocumentsByQuery(parsedQuery: IParsedQuery) { - if (parsedQuery.find) { - return Comment.find(parsedQuery.find).countDocuments(); - } - - return Comment.find().estimatedDocumentCount(); -} - -export async function findCommentById(id: string, populateField = { populate: "", limitPopulate: "" }) { - const comments = await Comment.findById(id).populate(populateField.populate, populateField.limitPopulate); - - if (!comments) { - return null; - } - - return comments; -} - -export async function createComment(data: ICreateCommentData) { - const result = new Comment(data); - await result.save(); - - return result; -} - -export async function deleteCommentById(id: string) { - await Comment.findOneAndDelete({ _id: id }); -} - -export async function saveUpdatedComment(comment: { save: () => void }) { - return comment.save(); -} diff --git a/src/features/comment/comment-repository.ts b/src/features/comment/comment-repository.ts new file mode 100644 index 0000000..75b1700 --- /dev/null +++ b/src/features/comment/comment-repository.ts @@ -0,0 +1,4 @@ +import { AppDataSource } from "../../config/db"; +import { Comment } from "./comment-entity"; + +export const commentRepository = () => AppDataSource.getRepository(Comment); diff --git a/src/features/comment/comment-schema.ts b/src/features/comment/comment-schema.ts deleted file mode 100644 index 10045f0..0000000 --- a/src/features/comment/comment-schema.ts +++ /dev/null @@ -1,35 +0,0 @@ -import mongoose, { Schema, Types, model } from "mongoose"; -import { IComment } from "./comment-types"; -import beautifyUnique from "mongoose-beautiful-unique-validation"; - -mongoose.Schema.Types.String.set("trim", true); - -const commentSchema = new Schema( - { - content: { - type: String, - required: [true, "Comment can't be empty"], - }, - owner: { - type: Types.ObjectId, - ref: "User", - required: [true, "Owner ID is required"], - }, - parent: { - type: mongoose.Schema.Types.ObjectId, - ref: "Comment", - }, - article: { - type: mongoose.Schema.Types.ObjectId, - ref: "Article", - required: [true, "Article ID is required"], - }, - }, - { timestamps: true } -); - -commentSchema.plugin(beautifyUnique); - -const Comment = model("Comment", commentSchema); - -export default Comment; diff --git a/src/features/comment/comment-service.ts b/src/features/comment/comment-service.ts index 2594a54..33c656c 100644 --- a/src/features/comment/comment-service.ts +++ b/src/features/comment/comment-service.ts @@ -1,33 +1,29 @@ import { NotFoundError, UnauthorizedError } from "../../utils/app-error"; -import { IFullQuery, parseQueryToMongoParams } from "../../utils/parse-query"; -import * as commentQueries from "./comment-queries"; -import { CommentDto, ICreateCommentData, IPatchCommentData } from "./comment-types"; +import { IFullQuery, buildQuery } from "../../utils/build-query"; +import { Comment } from "./comment-entity"; +import { commentRepository } from "./comment-repository"; +import { CommentDto, ICreateCommentData, IPutCommentData } from "./comment-types"; export async function getAllComments(query: IFullQuery): Promise { - const parsedQuery = parseQueryToMongoParams(query); + const comments = await buildQuery(commentRepository(), query); - if (parsedQuery.count) { - return commentQueries.countDocumentsByQuery(parsedQuery); + if (!comments) { + return []; } - const comments = await commentQueries.findCommentsByQuery(parsedQuery); - return comments.map((x) => new CommentDto(x)); } export async function getOneComment(id: string, query: any): Promise { - let populate = ""; - let limitPopulate = ""; + let queryBuilder = commentRepository().createQueryBuilder("entity"); - if (query && query.populate) { - populate += query.populate; + queryBuilder = queryBuilder.where("entity.id = :id", { id }); - if (query.populate.includes("owner")) { - limitPopulate += "username firstName lastName"; - } + if (query && query.populate) { + queryBuilder = queryBuilder.leftJoinAndSelect(`entity.${query.populate}`, query.populate); } - const comment = await commentQueries.findCommentById(id, { populate, limitPopulate }); + const comment = await queryBuilder.getOne(); if (!comment) { throw new NotFoundError(); @@ -36,18 +32,27 @@ export async function getOneComment(id: string, query: any): Promise { - const comment = await commentQueries.createComment(data); +export async function createComment(data: ICreateCommentData & { owner: string }): Promise { + const comment = new Comment(); + comment.article = data.article; + comment.parent = data.parent; + comment.content = data.content; + comment.created_at = new Date(); + comment.updated_at = new Date(); + comment.owner = data.owner; + + await commentRepository().save(comment); + return new CommentDto(comment); } const ALLOWED_UPDATE_FIELDS = ["content"]; -export async function updateComment(id: string, userId: string, data: IPatchCommentData): Promise { - const comment = await commentQueries.findCommentById(id); +export async function updateComment(id: string, userId: string, data: IPutCommentData): Promise { + const comment = await commentRepository().findOneBy({ id }); if (comment === null) throw new NotFoundError(); - if (comment.owner.toString() !== userId) throw new UnauthorizedError(); + if (comment.owner !== userId) throw new UnauthorizedError(); for (const key of ALLOWED_UPDATE_FIELDS) { if (key in data) { @@ -55,16 +60,18 @@ export async function updateComment(id: string, userId: string, data: IPatchComm } } - await commentQueries.saveUpdatedComment(comment); + comment.updated_at = new Date(); + + await commentRepository().save(comment); return new CommentDto(comment); } export async function deleteComment(id: string, userId: string): Promise { - const comment = await commentQueries.findCommentById(id); + const comment = await commentRepository().findOneBy({ id }); if (!comment) throw new NotFoundError(); - if (comment.owner.toString() !== userId) throw new UnauthorizedError(); + if (comment.owner !== userId) throw new UnauthorizedError(); - await commentQueries.deleteCommentById(id); + await commentRepository().remove(comment); } diff --git a/src/features/comment/comment-types.ts b/src/features/comment/comment-types.ts index ca38485..681da18 100644 --- a/src/features/comment/comment-types.ts +++ b/src/features/comment/comment-types.ts @@ -1,20 +1,13 @@ import { ObjectId, WithId } from "mongodb"; import { isValidObjectId } from "mongoose"; import { IUser, UserDto } from "../user/user-types"; +import { Comment } from "./comment-entity"; -export interface IComment { - _id: ObjectId; - content: string; - owner: ObjectId | IUser; - parent: ObjectId; - article: ObjectId; - createdAt: Date; - updatedAt: Date; -} - -export type ICreateCommentData = Pick; +export type ICreateCommentData = { content: string; parent: string; article: string }; -export type IPatchCommentData = Pick; +export type IPutCommentData = { + content: string; +}; /** * Represents a Public Comment with limited information. @@ -28,18 +21,18 @@ export class CommentDto { createdAt: Date; updatedAt: Date; - constructor(comment: WithId) { - this.id = comment._id.toString(); + constructor(comment: Comment) { + this.id = comment.id; this.content = comment.content; - this.parent = comment.parent?.toString(); - this.article = comment.article.toString(); - this.createdAt = comment.createdAt; - this.updatedAt = comment.updatedAt; + this.parent = comment.parent; + this.article = comment.article; + this.createdAt = comment.created_at; + this.updatedAt = comment.updated_at; - if (isValidObjectId(comment.owner)) { - this.owner = (comment.owner as ObjectId).toString(); + if (typeof comment.owner === "string") { + this.owner = comment.owner; } else { - this.owner = new UserDto(comment.owner as IUser); + this.owner = new UserDto(comment.owner); } } } diff --git a/src/features/comment/comment-validators.ts b/src/features/comment/comment-validators.ts index 68fb211..c0a3322 100644 --- a/src/features/comment/comment-validators.ts +++ b/src/features/comment/comment-validators.ts @@ -1,5 +1,5 @@ import Joi from "joi"; -import { ICreateCommentData, IPatchCommentData } from "./comment-types"; +import { ICreateCommentData, IPutCommentData } from "./comment-types"; export const commentCreateSchema = Joi.object().keys({ content: Joi.string().required().min(3).max(600).trim(), @@ -7,6 +7,6 @@ export const commentCreateSchema = Joi.object().keys({ article: Joi.string().required().trim(), }); -export const commentPatchSchema = Joi.object().keys({ +export const commentPatchSchema = Joi.object().keys({ content: Joi.string().required().min(3).max(600).trim(), }); diff --git a/src/features/user/user-entity.ts b/src/features/user/user-entity.ts new file mode 100644 index 0000000..2f3103b --- /dev/null +++ b/src/features/user/user-entity.ts @@ -0,0 +1,35 @@ +import bcrypt from "bcrypt"; + +import { Column, Entity, JoinColumn, OneToOne, PrimaryGeneratedColumn } from "typeorm"; + +@Entity() +export class User { + @PrimaryGeneratedColumn("uuid") + id: string; + + @Column({ type: "varchar", length: 200 }) + username: string; + + @Column({ type: "varchar", length: 200 }) + first_name: string; + + @Column({ type: "varchar", length: 200 }) + last_name: string; + + @Column({ type: "varchar", length: 200 }) + password: string; + + @Column({ type: "date" }) + updated_at: Date; + + @Column({ type: "date" }) + created_at: Date; + + public async comparePassword(password: string) { + return bcrypt.compare(password, this.password); + } + + public async setPassword(password: string) { + this.password = await bcrypt.hash(password, 10); + } +} diff --git a/src/features/user/user-queries.ts b/src/features/user/user-queries.ts deleted file mode 100644 index b98553a..0000000 --- a/src/features/user/user-queries.ts +++ /dev/null @@ -1,46 +0,0 @@ -import User from "./user-schema"; -import { ISignUpUserData, UserDto } from "./user-types"; - -export async function findOneByUsername(username: string | undefined) { - if (!username) return null; - - const existing = await User.findOne({ - username: username.toLowerCase(), - }); - - if (!existing) return null; - - return existing; -} - -export async function findOneById(id: string) { - const existing = await User.findById(id); - if (!existing) return null; - return existing; -} - -export async function findOneByPassword(username: string | undefined, password: string | undefined) { - if (!username || !password) return null; - - const existing = await User.findOne({ - username: username.toLowerCase(), - }); - - if (!existing) return null; - - const matching = await existing.comparePassword(password); - - if (!matching) return null; - - return existing; -} - -export async function createUser(userData: ISignUpUserData) { - const user = new User(userData); - await user.save(); - return user; -} - -export async function saveUpdatedUser(user: { save: () => void }) { - return user.save(); -} diff --git a/src/features/user/user-repository.ts b/src/features/user/user-repository.ts new file mode 100644 index 0000000..a21240e --- /dev/null +++ b/src/features/user/user-repository.ts @@ -0,0 +1,4 @@ +import { AppDataSource } from "../../config/db"; +import { User } from "./user-entity"; + +export const userRepository = () => AppDataSource.getRepository(User); diff --git a/src/features/user/user-schema.ts b/src/features/user/user-schema.ts deleted file mode 100644 index 88a8f04..0000000 --- a/src/features/user/user-schema.ts +++ /dev/null @@ -1,53 +0,0 @@ -import { Schema, model } from "mongoose"; -import bcrypt from "bcrypt"; -import { IUser } from "./user-types"; -import beautifyUnique from "mongoose-beautiful-unique-validation"; -import { BadRequestError } from "../../utils/app-error"; - -Schema.Types.String.set("trim", true); - -const userSchema = new Schema( - { - username: { - type: String, - unique: true, - }, - firstName: { - type: String, - }, - lastName: { - type: String, - }, - password: { - type: String, - }, - }, - { timestamps: true } -); - -userSchema.methods.comparePassword = async function (password: string) { - return bcrypt.compare(password, this.password); -}; - -userSchema.pre("save", function (next) { - this.username = this.username.toLowerCase(); - next(); -}); - -userSchema.pre("save", async function (next) { - if (this.isModified("password")) { - if (!noBlacklistedChars(this.password)) throw new BadRequestError("Password should not contain invalid characters"); - - this.password = await bcrypt.hash(this.password, 10); - } - next(); -}); - -userSchema.plugin(beautifyUnique); - -function noBlacklistedChars(params: string) { - return /\W/.test(params) === false; -} -const User = model("User", userSchema); - -export default User; diff --git a/src/features/user/user-service.ts b/src/features/user/user-service.ts index 69845e7..233564f 100644 --- a/src/features/user/user-service.ts +++ b/src/features/user/user-service.ts @@ -1,26 +1,54 @@ import jwt from "jsonwebtoken"; import getConfig from "../../config/get-config"; import { IUserLocal, ISignUpUserData, UserDto, IUpdateUserData } from "./user-types"; -import * as userQueries from "./user-queries"; import { BadRequestError, InternalServerError, NotFoundError, UnauthorizedError } from "../../utils/app-error"; +import { User } from "./user-entity"; +import { userRepository } from "./user-repository"; export async function signUp(userData: ISignUpUserData): Promise<[string, UserDto]> { - const existing = await userQueries.findOneByUsername(userData.username); + if (!userData.username) { + throw new BadRequestError(); + } + + const existing = await userRepository().findOneBy({ + username: userData.username.toLowerCase(), + }); if (existing) { throw new BadRequestError("Username already exists"); } - const user = await userQueries.createUser(userData); + const user = new User(); + user.username = userData.username; + user.first_name = userData.firstName; + user.last_name = userData.lastName; + user.setPassword(userData.password); + user.created_at = new Date(); + user.updated_at = new Date(); + + await userRepository().save(user); + const token = await createToken(user.id); return [token, new UserDto(user)]; } export async function signIn(username: string, password: string): Promise<[string, UserDto]> { - const user = await userQueries.findOneByPassword(username, password); + if (!username || !password) { + throw new BadRequestError(); + } + + const user = await userRepository().findOneBy({ + username: username.toLowerCase(), + }); if (!user) { + throw new UnauthorizedError(); + } + + const matchingPassword = await user.comparePassword(password); + + if (!matchingPassword) { throw new UnauthorizedError("Incorrect username or password"); } @@ -42,8 +70,7 @@ async function createToken(id: string) { } export async function getUser(userData: IUserLocal): Promise { - const user = await userQueries.findOneById(userData.id); - + const user = await userRepository().findOneBy({ id: userData.id }); if (!user) { throw new NotFoundError(); } @@ -54,9 +81,9 @@ export async function getUser(userData: IUserLocal): Promise { const ALLOWED_UPDATE_FIELDS = ["firstName", "lastName"]; export async function updateUser(userId: string, userData: Partial): Promise { - const user = await userQueries.findOneById(userId); + const user = await userRepository().findOneBy({ id: userId }); - if (user === null || user._id.toString() !== userId) { + if (!user || user.id !== userId) { throw new UnauthorizedError(); } @@ -66,14 +93,15 @@ export async function updateUser(userId: string, userData: Partial console.log(`⚡️ Server is listening on port ${port}...`)); + AppDataSource.initialize() + .then(async () => { + app.listen(port, () => console.log(`⚡️ Server is listening on port ${port}...`)); + }) + .catch((err) => { + console.error("Error connecting to database", err); + process.exit(1); + }); })(); // Uncaught exception and SIGTERM handling diff --git a/src/middleware/error-middleware.ts b/src/middleware/error-middleware.ts index c48825d..5319a93 100644 --- a/src/middleware/error-middleware.ts +++ b/src/middleware/error-middleware.ts @@ -6,7 +6,7 @@ import { IJsonResponse } from "../utils/json-response"; const errorMiddleware = () => (err: AppError, req: Request, res: Response, next: NextFunction) => { const { statusCode = HttpStatusCode.INTERNAL_SERVER_ERROR, message = "Something went wrong" } = err; - console.error(`Code: ${statusCode}; Error: ${message}`); + console.error(`Code: ${statusCode}; Error: `, err); res.status(statusCode).json({ code: statusCode, diff --git a/src/utils/build-query.ts b/src/utils/build-query.ts new file mode 100644 index 0000000..acc3f0d --- /dev/null +++ b/src/utils/build-query.ts @@ -0,0 +1,73 @@ +export interface IFullQuery { + where?: string | string[]; + sortBy?: string | string[]; + page?: string; + pageSize?: string; + populate?: string | string[]; + select?: string | string[]; + count?: string; +} + +import { Repository } from "typeorm"; + +export interface IFullQuery { + where?: string | string[]; + sortBy?: string | string[]; + page?: string; + pageSize?: string; + populate?: string | string[]; + select?: string | string[]; + count?: string; +} + +export async function buildQuery(repository: Repository, query: IFullQuery): Promise { + let queryBuilder = repository.createQueryBuilder("entity"); + + if (query.where) { + if (Array.isArray(query.where)) { + query.where.forEach((condition: string) => { + queryBuilder = queryBuilder.andWhere(condition); + }); + } else { + queryBuilder = queryBuilder.where(query.where); + } + } + + if (query.sortBy) { + if (Array.isArray(query.sortBy)) { + query.sortBy.forEach((sortCondition: string) => { + queryBuilder = queryBuilder.addOrderBy(sortCondition); + }); + } else { + queryBuilder = queryBuilder.orderBy(query.sortBy); + } + } + + if (query.page && query.pageSize) { + const page = parseInt(query.page); + const pageSize = parseInt(query.pageSize); + queryBuilder = queryBuilder.skip((page - 1) * pageSize).take(pageSize); + } + + if (query.populate) { + if (Array.isArray(query.populate)) { + query.populate.forEach((relation: string) => { + queryBuilder = queryBuilder.leftJoinAndSelect(`entity.${relation}`, relation); + }); + } else { + queryBuilder = queryBuilder.leftJoinAndSelect(`entity.${query.populate}`, query.populate); + } + } + + if (query.select) { + if (Array.isArray(query.select)) { + query.select.forEach((field: string) => { + queryBuilder = queryBuilder.addSelect(`entity.${field}`); + }); + } else { + queryBuilder = queryBuilder.select(query.select); + } + } + + return await queryBuilder.getMany(); +} diff --git a/src/utils/parse-query.ts b/src/utils/parse-query.ts deleted file mode 100644 index 70f7e91..0000000 --- a/src/utils/parse-query.ts +++ /dev/null @@ -1,97 +0,0 @@ -import { SortOrder } from "mongoose"; - -export interface IFullQuery { - where?: string | string[]; - sortBy?: string | string[]; - page?: string; - pageSize?: string; - populate?: string | string[]; - select?: string | string[]; - count?: string; -} - -export interface IParsedQuery { - find: { [key: string]: string | string[] }; - sort: { [key: string]: SortOrder | { $meta: any } }; - pagination: { - limit: number; - skip: number; - }; - populate: string; - limitPopulate: string; - select: string; - count: boolean; -} - -export function parseQueryToMongoParams(query: IFullQuery): IParsedQuery { - const parsedQuery: IParsedQuery = { - find: {}, - sort: {}, - pagination: { - limit: 10, - skip: 0, - }, - populate: "", - limitPopulate: "", - select: "", - count: false, - }; - - // Search - if (Array.isArray(query.where)) { - query.where.forEach((x) => { - const [prop, value] = x.split("="); - if (!parsedQuery.find[prop]) { - parsedQuery.find[prop] = []; - } - - (parsedQuery.find[prop] as string[]).push(value); - }); - } else if (typeof query.where === "string") { - const [prop, value] = query.where.split("="); - parsedQuery.find[prop] = value; - } - - // Sort - if (Array.isArray(query.sortBy)) { - query.sortBy.forEach((x) => { - const [sortProp, order] = x.split(" "); - parsedQuery.sort[sortProp] = order as SortOrder; - }); - } else if (typeof query.sortBy === "string") { - const [sortProp, order] = query.sortBy.split(" "); - parsedQuery.sort[sortProp] = order as SortOrder; - } else { - parsedQuery.sort = { createdAt: "asc" }; - } - - if (query.page && query.pageSize) { - parsedQuery.pagination.limit = parseInt(query.pageSize); - parsedQuery.pagination.skip = Math.max(0, parseInt(query.page) - 1) * parsedQuery.pagination.limit; - } - - // Populate properties - if (Array.isArray(query.populate)) { - parsedQuery.populate += query.populate.join(" "); - } else if (typeof query.populate === "string") { - parsedQuery.populate += query.populate; - } - - if (query.populate && query.populate.includes("owner")) { - parsedQuery.limitPopulate += "username firstName lastName"; - } - - // Select properties - if (Array.isArray(query.select)) { - parsedQuery.select += query.select.join(" "); - } else if (typeof query.select === "string") { - parsedQuery.select += query.select; - } - - // Return count if specified - if (query.count === "true") { - parsedQuery.count = true; - } - - return parsedQuery; -} diff --git a/tsconfig.json b/tsconfig.json index e5b0e69..8d624ef 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -16,8 +16,8 @@ "es6" ] /* Specify a set of bundled library declaration files that describe the target runtime environment. */, // "jsx": "preserve", /* Specify what JSX code is generated. */ - // "experimentalDecorators": true, /* Enable experimental support for legacy experimental decorators. */ - // "emitDecoratorMetadata": true, /* Emit design-type metadata for decorated declarations in source files. */ + "experimentalDecorators": true /* Enable experimental support for legacy experimental decorators. */, + "emitDecoratorMetadata": true /* Emit design-type metadata for decorated declarations in source files. */, // "jsxFactory": "", /* Specify the JSX factory function used when targeting React JSX emit, e.g. 'React.createElement' or 'h'. */ // "jsxFragmentFactory": "", /* Specify the JSX Fragment reference used for fragments when targeting React JSX emit e.g. 'React.Fragment' or 'Fragment'. */ // "jsxImportSource": "", /* Specify module specifier used to import the JSX factory functions when using 'jsx: react-jsx*'. */