From 92f40789d79c8caf232a366b84049f0c7bd3edb7 Mon Sep 17 00:00:00 2001 From: Ishan-ttpl Date: Wed, 21 May 2025 14:36:21 +0530 Subject: [PATCH 01/22] Update Dev.yml --- .github/workflows/Dev.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/Dev.yml b/.github/workflows/Dev.yml index 20f9f1d..04c1023 100644 --- a/.github/workflows/Dev.yml +++ b/.github/workflows/Dev.yml @@ -2,7 +2,7 @@ name: DEV DEPLOYMENT on: push: - branches: [ test ] + branches: [ lais-v2.0 ] jobs: build: runs-on: ubuntu-latest From bd480ee010bb3554b3c4076cd36d608fe4b80080 Mon Sep 17 00:00:00 2001 From: DevendraPPatil Date: Mon, 9 Jun 2025 18:23:40 +0530 Subject: [PATCH 02/22] Fix :1.security policy added 2.Formating chnages done --- package-lock.json | 267 +------ src/app-cluster.service.ts | 28 +- src/app.controller.ts | 16 +- src/auth/auth.guard.ts | 28 +- src/auth/auth.module.ts | 6 +- src/config/commonConfig.ts | 334 ++++----- src/config/language/en.ts | 180 ++--- src/config/language/hi.ts | 8 +- src/config/language/kn.ts | 8 +- src/config/language/ta.ts | 8 +- src/config/language/te.ts | 8 +- src/controllers/collection.controller.ts | 102 ++- src/controllers/content.controller.ts | 553 +++++++++----- src/main.ts | 16 +- src/schemas/content.schema.ts | 34 +- src/services/collection.service.ts | 37 +- src/services/content.service.ts | 876 +++++++++++++---------- 17 files changed, 1308 insertions(+), 1201 deletions(-) diff --git a/package-lock.json b/package-lock.json index fab797c..cac1599 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,7 +9,6 @@ "version": "0.0.1", "license": "UNLICENSED", "dependencies": { - "@fastify/compress": "^7.0.3", "@fastify/static": "^6.12.0", "@nestjs/axios": "^3.0.1", "@nestjs/common": "^10.0.0", @@ -975,67 +974,6 @@ "node": ">=14" } }, - "node_modules/@fastify/compress": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/@fastify/compress/-/compress-7.0.3.tgz", - "integrity": "sha512-xa9fo5/DgK1s0bkS6xrYgNn8HmofO5tJvbCDk8QuXshSgLd2cFZANv1ox/Qv7zswS7JroHwTlCVv/XGTVO98tg==", - "dependencies": { - "@fastify/accept-negotiator": "^1.1.0", - "fastify-plugin": "^4.5.0", - "mime-db": "^1.52.0", - "minipass": "^7.0.2", - "peek-stream": "^1.1.3", - "pump": "^3.0.0", - "pumpify": "^2.0.1", - "readable-stream": "^4.5.2" - } - }, - "node_modules/@fastify/compress/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/@fastify/compress/node_modules/readable-stream": { - "version": "4.5.2", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-4.5.2.tgz", - "integrity": "sha512-yjavECdqeZ3GLXNgRXgeQEdz9fvDDkNKyHnbHRFtOr7/LcfgBcmct7t/ET+HaCTqfh06OzoAxrkN/IfjJBVe+g==", - "dependencies": { - "abort-controller": "^3.0.0", - "buffer": "^6.0.3", - "events": "^3.3.0", - "process": "^0.11.10", - "string_decoder": "^1.3.0" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - } - }, - "node_modules/@fastify/compress/node_modules/string_decoder": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", - "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", - "dependencies": { - "safe-buffer": "~5.2.0" - } - }, "node_modules/@fastify/cors": { "version": "9.0.1", "resolved": "https://registry.npmjs.org/@fastify/cors/-/cors-9.0.1.tgz", @@ -4223,17 +4161,6 @@ "node": ">=12" } }, - "node_modules/duplexify": { - "version": "3.7.1", - "resolved": "https://registry.npmjs.org/duplexify/-/duplexify-3.7.1.tgz", - "integrity": "sha512-07z8uv2wMyS51kKhD1KsdXJg5WQ6t93RneqRxUHnskXVtlYYkLqM0gqStQZ3pj073g687jPCHrqNfCzawLYh5g==", - "dependencies": { - "end-of-stream": "^1.0.0", - "inherits": "^2.0.1", - "readable-stream": "^2.0.0", - "stream-shift": "^1.0.0" - } - }, "node_modules/eastasianwidth": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", @@ -7079,6 +7006,7 @@ "version": "7.0.4", "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.0.4.tgz", "integrity": "sha512-jYofLM5Dam9279rdkWzqHozUo4ybjdZmCsDHePy5V/PbBcVMiSZR97gmAy45aqi8CK1lG2ECd356FU86avfwUQ==", + "dev": true, "engines": { "node": ">=16 || 14 >=14.17" } @@ -7637,16 +7565,6 @@ "integrity": "sha512-KG8UEiEVkR3wGEb4m5yZkVCzigAD+cVEJck2CzYZO37ZGJfctvVptVO192MwrtPhzONn6go8ylnOdMhKqi4nfg==", "peer": true }, - "node_modules/peek-stream": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/peek-stream/-/peek-stream-1.1.3.tgz", - "integrity": "sha512-FhJ+YbOSBb9/rIl2ZeE/QHEsWn7PqNYt8ARAY3kIgNGOk13g9FGyIY6JIl/xB/3TFRVoTv5as0l11weORrTekA==", - "dependencies": { - "buffer-from": "^1.0.0", - "duplexify": "^3.5.0", - "through2": "^2.0.3" - } - }, "node_modules/picocolors": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", @@ -7938,49 +7856,6 @@ "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==" }, - "node_modules/pump": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", - "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", - "dependencies": { - "end-of-stream": "^1.1.0", - "once": "^1.3.1" - } - }, - "node_modules/pumpify": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/pumpify/-/pumpify-2.0.1.tgz", - "integrity": "sha512-m7KOje7jZxrmutanlkS1daj1dS6z6BgslzOXmcSEpIlCxM3VJH7lG5QLeck/6hgF6F4crFf01UtQmNsJfweTAw==", - "dependencies": { - "duplexify": "^4.1.1", - "inherits": "^2.0.3", - "pump": "^3.0.0" - } - }, - "node_modules/pumpify/node_modules/duplexify": { - "version": "4.1.3", - "resolved": "https://registry.npmjs.org/duplexify/-/duplexify-4.1.3.tgz", - "integrity": "sha512-M3BmBhwJRZsSx38lZyhE53Csddgzl5R7xGJNk7CVddZD6CcmwMCH8J+7AprIrQKH7TonKxaCjcv27Qmf+sQ+oA==", - "dependencies": { - "end-of-stream": "^1.4.1", - "inherits": "^2.0.3", - "readable-stream": "^3.1.1", - "stream-shift": "^1.0.2" - } - }, - "node_modules/pumpify/node_modules/readable-stream": { - "version": "3.6.2", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", - "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", - "dependencies": { - "inherits": "^2.0.3", - "string_decoder": "^1.1.1", - "util-deprecate": "^1.0.1" - }, - "engines": { - "node": ">= 6" - } - }, "node_modules/punycode": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", @@ -8815,11 +8690,6 @@ "node": ">= 0.8" } }, - "node_modules/stream-shift": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/stream-shift/-/stream-shift-1.0.3.tgz", - "integrity": "sha512-76ORR0DO1o1hlKwTbi/DM3EXWGf3ZJYO8cXX5RJwnul2DEg2oyoZyjLNoQM8WsvZiFKCRfC1O0J7iCvie3RZmQ==" - }, "node_modules/stream-wormhole": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/stream-wormhole/-/stream-wormhole-1.1.0.tgz", @@ -9184,15 +9054,6 @@ "integrity": "sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==", "dev": true }, - "node_modules/through2": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/through2/-/through2-2.0.5.tgz", - "integrity": "sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ==", - "dependencies": { - "readable-stream": "~2.3.6", - "xtend": "~4.0.1" - } - }, "node_modules/tmp": { "version": "0.0.33", "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz", @@ -10559,52 +10420,6 @@ "text-decoding": "^1.0.0" } }, - "@fastify/compress": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/@fastify/compress/-/compress-7.0.3.tgz", - "integrity": "sha512-xa9fo5/DgK1s0bkS6xrYgNn8HmofO5tJvbCDk8QuXshSgLd2cFZANv1ox/Qv7zswS7JroHwTlCVv/XGTVO98tg==", - "requires": { - "@fastify/accept-negotiator": "^1.1.0", - "fastify-plugin": "^4.5.0", - "mime-db": "^1.52.0", - "minipass": "^7.0.2", - "peek-stream": "^1.1.3", - "pump": "^3.0.0", - "pumpify": "^2.0.1", - "readable-stream": "^4.5.2" - }, - "dependencies": { - "buffer": { - "version": "6.0.3", - "resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz", - "integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==", - "requires": { - "base64-js": "^1.3.1", - "ieee754": "^1.2.1" - } - }, - "readable-stream": { - "version": "4.5.2", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-4.5.2.tgz", - "integrity": "sha512-yjavECdqeZ3GLXNgRXgeQEdz9fvDDkNKyHnbHRFtOr7/LcfgBcmct7t/ET+HaCTqfh06OzoAxrkN/IfjJBVe+g==", - "requires": { - "abort-controller": "^3.0.0", - "buffer": "^6.0.3", - "events": "^3.3.0", - "process": "^0.11.10", - "string_decoder": "^1.3.0" - } - }, - "string_decoder": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", - "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", - "requires": { - "safe-buffer": "~5.2.0" - } - } - } - }, "@fastify/cors": { "version": "9.0.1", "resolved": "https://registry.npmjs.org/@fastify/cors/-/cors-9.0.1.tgz", @@ -12986,17 +12801,6 @@ "resolved": "https://registry.npmjs.org/dotenv-expand/-/dotenv-expand-10.0.0.tgz", "integrity": "sha512-GopVGCpVS1UKH75VKHGuQFqS1Gusej0z4FyQkPdwjil2gNIv+LNsqBlboOzpJFZKVT95GkCyWJbBSdFEFUWI2A==" }, - "duplexify": { - "version": "3.7.1", - "resolved": "https://registry.npmjs.org/duplexify/-/duplexify-3.7.1.tgz", - "integrity": "sha512-07z8uv2wMyS51kKhD1KsdXJg5WQ6t93RneqRxUHnskXVtlYYkLqM0gqStQZ3pj073g687jPCHrqNfCzawLYh5g==", - "requires": { - "end-of-stream": "^1.0.0", - "inherits": "^2.0.1", - "readable-stream": "^2.0.0", - "stream-shift": "^1.0.0" - } - }, "eastasianwidth": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", @@ -15162,7 +14966,8 @@ "minipass": { "version": "7.0.4", "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.0.4.tgz", - "integrity": "sha512-jYofLM5Dam9279rdkWzqHozUo4ybjdZmCsDHePy5V/PbBcVMiSZR97gmAy45aqi8CK1lG2ECd356FU86avfwUQ==" + "integrity": "sha512-jYofLM5Dam9279rdkWzqHozUo4ybjdZmCsDHePy5V/PbBcVMiSZR97gmAy45aqi8CK1lG2ECd356FU86avfwUQ==", + "dev": true }, "mkdirp": { "version": "0.5.6", @@ -15568,16 +15373,6 @@ "integrity": "sha512-KG8UEiEVkR3wGEb4m5yZkVCzigAD+cVEJck2CzYZO37ZGJfctvVptVO192MwrtPhzONn6go8ylnOdMhKqi4nfg==", "peer": true }, - "peek-stream": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/peek-stream/-/peek-stream-1.1.3.tgz", - "integrity": "sha512-FhJ+YbOSBb9/rIl2ZeE/QHEsWn7PqNYt8ARAY3kIgNGOk13g9FGyIY6JIl/xB/3TFRVoTv5as0l11weORrTekA==", - "requires": { - "buffer-from": "^1.0.0", - "duplexify": "^3.5.0", - "through2": "^2.0.3" - } - }, "picocolors": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", @@ -15792,48 +15587,6 @@ "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==" }, - "pump": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", - "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", - "requires": { - "end-of-stream": "^1.1.0", - "once": "^1.3.1" - } - }, - "pumpify": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/pumpify/-/pumpify-2.0.1.tgz", - "integrity": "sha512-m7KOje7jZxrmutanlkS1daj1dS6z6BgslzOXmcSEpIlCxM3VJH7lG5QLeck/6hgF6F4crFf01UtQmNsJfweTAw==", - "requires": { - "duplexify": "^4.1.1", - "inherits": "^2.0.3", - "pump": "^3.0.0" - }, - "dependencies": { - "duplexify": { - "version": "4.1.3", - "resolved": "https://registry.npmjs.org/duplexify/-/duplexify-4.1.3.tgz", - "integrity": "sha512-M3BmBhwJRZsSx38lZyhE53Csddgzl5R7xGJNk7CVddZD6CcmwMCH8J+7AprIrQKH7TonKxaCjcv27Qmf+sQ+oA==", - "requires": { - "end-of-stream": "^1.4.1", - "inherits": "^2.0.3", - "readable-stream": "^3.1.1", - "stream-shift": "^1.0.2" - } - }, - "readable-stream": { - "version": "3.6.2", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", - "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", - "requires": { - "inherits": "^2.0.3", - "string_decoder": "^1.1.1", - "util-deprecate": "^1.0.1" - } - } - } - }, "punycode": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", @@ -16451,11 +16204,6 @@ "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==" }, - "stream-shift": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/stream-shift/-/stream-shift-1.0.3.tgz", - "integrity": "sha512-76ORR0DO1o1hlKwTbi/DM3EXWGf3ZJYO8cXX5RJwnul2DEg2oyoZyjLNoQM8WsvZiFKCRfC1O0J7iCvie3RZmQ==" - }, "stream-wormhole": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/stream-wormhole/-/stream-wormhole-1.1.0.tgz", @@ -16723,15 +16471,6 @@ "integrity": "sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==", "dev": true }, - "through2": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/through2/-/through2-2.0.5.tgz", - "integrity": "sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ==", - "requires": { - "readable-stream": "~2.3.6", - "xtend": "~4.0.1" - } - }, "tmp": { "version": "0.0.33", "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz", diff --git a/src/app-cluster.service.ts b/src/app-cluster.service.ts index 85c7b13..36b2444 100644 --- a/src/app-cluster.service.ts +++ b/src/app-cluster.service.ts @@ -6,19 +6,19 @@ const numCPUs = os.cpus().length; @Injectable() export class AppClusterService { - static clusterize(callback: Function): void { - if (cluster.isMaster) { - console.log(`Master server started on ${process.pid}`); - for (let i = 0; i < numCPUs; i++) { - cluster.fork(); - } - cluster.on('exit', (worker, code, signal) => { - console.log(`Worker ${worker.process.pid} died. Restarting`); - cluster.fork(); - }) - } else { - console.log(`Cluster server started on ${process.pid}`) - callback(); - } + static clusterize(callback: Function): void { + if (cluster.isMaster) { + console.log(`Master server started on ${process.pid}`); + for (let i = 0; i < numCPUs; i++) { + cluster.fork(); + } + cluster.on('exit', (worker, code, signal) => { + console.log(`Worker ${worker.process.pid} died. Restarting`); + cluster.fork(); + }); + } else { + console.log(`Cluster server started on ${process.pid}`); + callback(); } + } } diff --git a/src/app.controller.ts b/src/app.controller.ts index f5b026d..8b45b8b 100644 --- a/src/app.controller.ts +++ b/src/app.controller.ts @@ -11,12 +11,12 @@ export class AppController { return this.appService.getHello(); } - // Health check endpoint - @Get('/ping') - checkHealth(): { status: boolean; message: string } { - return { - status: true, - message: 'content ServiceApp is working', - }; - } + // Health check endpoint + @Get('/ping') + checkHealth(): { status: boolean; message: string } { + return { + status: true, + message: 'content ServiceApp is working', + }; + } } diff --git a/src/auth/auth.guard.ts b/src/auth/auth.guard.ts index 82d88da..4c7aa6c 100644 --- a/src/auth/auth.guard.ts +++ b/src/auth/auth.guard.ts @@ -1,15 +1,19 @@ -import { Injectable, CanActivate, ExecutionContext, UnauthorizedException } from '@nestjs/common'; +import { + Injectable, + CanActivate, + ExecutionContext, + UnauthorizedException, +} from '@nestjs/common'; import { JwtService } from '@nestjs/jwt'; import { createHash } from 'crypto'; import { Request } from 'express'; -import * as jose from 'jose' +import * as jose from 'jose'; @Injectable() export class JwtAuthGuard implements CanActivate { - constructor(private jwtService: JwtService) { } + constructor(private jwtService: JwtService) {} async canActivate(context: ExecutionContext): Promise { - const request = context.switchToHttp().getRequest(); const authHeader = request.headers.authorization; if (!authHeader) { @@ -20,21 +24,23 @@ export class JwtAuthGuard implements CanActivate { //Step 1: Correctly Generate Encryption Key const secret_key = process.env.JOSE_SECRET || ''; const hash = createHash('sha256').update(secret_key).digest(); - + //Step 2: Decrypt the Token const jwtDecryptedToken = await jose.jwtDecrypt(token, hash); - + if (!jwtDecryptedToken.payload.jwtSignedToken) { - throw new Error("jwtSignedToken not found in decrypted payload"); + throw new Error('jwtSignedToken not found in decrypted payload'); } //Step 3: Verify the Signed JWT const jwtSignedToken = String(jwtDecryptedToken.payload.jwtSignedToken); - + //Fix Signing Key - const jwtSigninKey = new TextEncoder().encode(process.env.JWT_SIGNIN_PRIVATE_KEY); + const jwtSigninKey = new TextEncoder().encode( + process.env.JWT_SIGNIN_PRIVATE_KEY, + ); const verifiedToken = await jose.jwtVerify(jwtSignedToken, jwtSigninKey); - + //Step 4: Attach User Data to Request (request as any).user = verifiedToken.payload; @@ -43,4 +49,4 @@ export class JwtAuthGuard implements CanActivate { throw new UnauthorizedException('Invalid or expired token'); } } -} \ No newline at end of file +} diff --git a/src/auth/auth.module.ts b/src/auth/auth.module.ts index 9d08bfe..7e318c0 100644 --- a/src/auth/auth.module.ts +++ b/src/auth/auth.module.ts @@ -7,10 +7,10 @@ import { JwtAuthGuard } from './auth.guard'; imports: [ PassportModule, JwtModule.register({ - secret: process.env.JOSE_SECRET + secret: process.env.JOSE_SECRET, }), ], - providers: [JwtAuthGuard,JwtService], + providers: [JwtAuthGuard, JwtService], exports: [JwtAuthGuard, JwtService], }) -export class AuthModule {} \ No newline at end of file +export class AuthModule {} diff --git a/src/config/commonConfig.ts b/src/config/commonConfig.ts index 18d4d3c..85728aa 100644 --- a/src/config/commonConfig.ts +++ b/src/config/commonConfig.ts @@ -1,170 +1,170 @@ var common_config = { - contentLevel: [ - { - level: 'L1', - syllableCount: { $eq: 2 }, - language: 'ta', - contentType: 'Word', - }, - { - level: 'L1', - wordCount: { $gte: 2, $lte: 3 }, - language: 'ta', - contentType: 'Sentence', - }, - { - level: 'L2', - syllableCount: { $gte: 2, $lte: 3 }, - language: 'ta', - contentType: 'Word', - }, - { - level: 'L2', - wordCount: { $gte: 2, $lte: 3 }, - syllableCount: { $lte: 8 }, - syllableCountArray: { - $not: { - $elemMatch: { - v: { $gte: 4 }, - }, - }, - }, - language: 'ta', - contentType: 'Sentence', - }, - { - level: 'L3', - syllableCount: { $gte: 4 }, - language: 'ta', - contentType: 'Word', - }, - { - level: 'L3', - wordCount: { $gt: 2, $lte: 5 }, - syllableCount: { $lte: 15 }, - language: 'ta', - syllableCountArray: { - $not: { - $elemMatch: { - v: { $gte: 5 }, - }, - }, - }, - contentType: 'Sentence', - }, - { - level: 'L3', - wordCount: { $lte: 10 }, - language: 'ta', - contentType: 'Paragraph', - }, - { - level: 'L4', - wordCount: { $gt: 5, $lte: 7 }, - syllableCount: { $lte: 20 }, - language: 'ta', - syllableCountArray: { - $not: { - $elemMatch: { - v: { $gte: 7 }, - }, - }, - }, - contentType: 'Sentence', - }, - { - level: 'L4', - wordCount: { $lte: 10 }, - language: 'ta', - contentType: 'Paragraph', - }, - { - level: 'L5', - wordCount: { $gte: 7, $lte: 10 }, - language: 'ta', - contentType: 'Sentence', - }, - { - level: 'L5', - wordCount: { $gt: 10, $lte: 15 }, - language: 'ta', - contentType: 'Paragraph', - }, - { - level: 'L6', - wordCount: { $gte: 7, $lte: 12 }, - language: 'ta', - contentType: 'Sentence', - }, - { - level: 'L6', - wordCount: { $gt: 15 }, - language: 'ta', - contentType: 'Paragraph', - }, - ], - complexity: [ - { - level: 'C1', - totalOrthoComplexity: { $gte: 0, $lte: 2 }, - totalPhonicComplexity: { $gte: 0, $lte: 30 }, - language: 'ta', - contentType: 'Word', - }, - { - level: 'C1', - totalOrthoComplexity: { $gte: 0, $lte: 75 }, - totalPhonicComplexity: { $gte: 0, $lte: 20 }, - meanComplexity: { $gte: 0, $lte: 50 }, - language: 'ta', - contentType: 'Sentence', - }, - { - level: 'C2', - totalOrthoComplexity: { $gte: 0, $lte: 8 }, - totalPhonicComplexity: { $gte: 0, $lte: 60 }, - language: 'ta', - contentType: 'Word', - }, - { - level: 'C2', - totalOrthoComplexity: { $gte: 0, $lte: 20 }, - totalPhonicComplexity: { $gte: 0, $lte: 100 }, - meanComplexity: { $gte: 0, $lte: 50 }, - language: 'ta', - contentType: 'Sentence', - }, - { - level: 'C3', - totalOrthoComplexity: { $gte: 0, $lte: 15 }, - totalPhonicComplexity: { $gte: 0, $lte: 100 }, - language: 'ta', - contentType: 'Word', - }, - { - level: 'C3', - totalOrthoComplexity: { $gte: 20, $lte: 50 }, - totalPhonicComplexity: { $lte: 200 }, - meanComplexity: { $gte: 50, $lte: 100 }, - language: 'ta', - contentType: 'Sentence', - }, - { - level: 'C4', - totalOrthoComplexity: { $gt: 15 }, - totalPhonicComplexity: { $gt: 100 }, - language: 'ta', - contentType: 'Word', - }, - { - level: 'C4', - totalOrthoComplexity: { $gt: 50 }, - totalPhonicComplexity: { $gt: 200 }, - meanComplexity: { $gt: 100 }, - language: 'ta', - contentType: 'Sentence', - }, - ] + contentLevel: [ + { + level: 'L1', + syllableCount: { $eq: 2 }, + language: 'ta', + contentType: 'Word', + }, + { + level: 'L1', + wordCount: { $gte: 2, $lte: 3 }, + language: 'ta', + contentType: 'Sentence', + }, + { + level: 'L2', + syllableCount: { $gte: 2, $lte: 3 }, + language: 'ta', + contentType: 'Word', + }, + { + level: 'L2', + wordCount: { $gte: 2, $lte: 3 }, + syllableCount: { $lte: 8 }, + syllableCountArray: { + $not: { + $elemMatch: { + v: { $gte: 4 }, + }, + }, + }, + language: 'ta', + contentType: 'Sentence', + }, + { + level: 'L3', + syllableCount: { $gte: 4 }, + language: 'ta', + contentType: 'Word', + }, + { + level: 'L3', + wordCount: { $gt: 2, $lte: 5 }, + syllableCount: { $lte: 15 }, + language: 'ta', + syllableCountArray: { + $not: { + $elemMatch: { + v: { $gte: 5 }, + }, + }, + }, + contentType: 'Sentence', + }, + { + level: 'L3', + wordCount: { $lte: 10 }, + language: 'ta', + contentType: 'Paragraph', + }, + { + level: 'L4', + wordCount: { $gt: 5, $lte: 7 }, + syllableCount: { $lte: 20 }, + language: 'ta', + syllableCountArray: { + $not: { + $elemMatch: { + v: { $gte: 7 }, + }, + }, + }, + contentType: 'Sentence', + }, + { + level: 'L4', + wordCount: { $lte: 10 }, + language: 'ta', + contentType: 'Paragraph', + }, + { + level: 'L5', + wordCount: { $gte: 7, $lte: 10 }, + language: 'ta', + contentType: 'Sentence', + }, + { + level: 'L5', + wordCount: { $gt: 10, $lte: 15 }, + language: 'ta', + contentType: 'Paragraph', + }, + { + level: 'L6', + wordCount: { $gte: 7, $lte: 12 }, + language: 'ta', + contentType: 'Sentence', + }, + { + level: 'L6', + wordCount: { $gt: 15 }, + language: 'ta', + contentType: 'Paragraph', + }, + ], + complexity: [ + { + level: 'C1', + totalOrthoComplexity: { $gte: 0, $lte: 2 }, + totalPhonicComplexity: { $gte: 0, $lte: 30 }, + language: 'ta', + contentType: 'Word', + }, + { + level: 'C1', + totalOrthoComplexity: { $gte: 0, $lte: 75 }, + totalPhonicComplexity: { $gte: 0, $lte: 20 }, + meanComplexity: { $gte: 0, $lte: 50 }, + language: 'ta', + contentType: 'Sentence', + }, + { + level: 'C2', + totalOrthoComplexity: { $gte: 0, $lte: 8 }, + totalPhonicComplexity: { $gte: 0, $lte: 60 }, + language: 'ta', + contentType: 'Word', + }, + { + level: 'C2', + totalOrthoComplexity: { $gte: 0, $lte: 20 }, + totalPhonicComplexity: { $gte: 0, $lte: 100 }, + meanComplexity: { $gte: 0, $lte: 50 }, + language: 'ta', + contentType: 'Sentence', + }, + { + level: 'C3', + totalOrthoComplexity: { $gte: 0, $lte: 15 }, + totalPhonicComplexity: { $gte: 0, $lte: 100 }, + language: 'ta', + contentType: 'Word', + }, + { + level: 'C3', + totalOrthoComplexity: { $gte: 20, $lte: 50 }, + totalPhonicComplexity: { $lte: 200 }, + meanComplexity: { $gte: 50, $lte: 100 }, + language: 'ta', + contentType: 'Sentence', + }, + { + level: 'C4', + totalOrthoComplexity: { $gt: 15 }, + totalPhonicComplexity: { $gt: 100 }, + language: 'ta', + contentType: 'Word', + }, + { + level: 'C4', + totalOrthoComplexity: { $gt: 50 }, + totalPhonicComplexity: { $gt: 200 }, + meanComplexity: { $gt: 100 }, + language: 'ta', + contentType: 'Sentence', + }, + ], }; -export default common_config; \ No newline at end of file +export default common_config; diff --git a/src/config/language/en.ts b/src/config/language/en.ts index ced9480..529ad9a 100644 --- a/src/config/language/en.ts +++ b/src/config/language/en.ts @@ -1,96 +1,96 @@ var en_config = { - language_code: "en", - contentLevel: [ - { - level: 'L1', - syllableCount: { $gte: 2, $lte: 3 }, - contentType: 'Word', + language_code: 'en', + contentLevel: [ + { + level: 'L1', + syllableCount: { $gte: 2, $lte: 3 }, + contentType: 'Word', + }, + { + level: 'L1', + wordCount: { $gte: 2, $lte: 3 }, + contentType: 'Sentence', + }, + { + level: 'L2', + syllableCount: { $eq: 4 }, + contentType: 'Word', + }, + { + level: 'L2', + wordCount: { $gte: 2, $lte: 3 }, + syllableCount: { $lte: 8 }, + syllableCountArray: { + $not: { + $elemMatch: { + v: { $gte: 4 }, + }, }, - { - level: 'L1', - wordCount: { $gte: 2, $lte: 3 }, - contentType: 'Sentence', + }, + contentType: 'Sentence', + }, + { + level: 'L3', + syllableCount: { $gt: 4 }, + contentType: 'Word', + }, + { + level: 'L3', + wordCount: { $gt: 2, $lte: 5 }, + syllableCount: { $lte: 15 }, + syllableCountArray: { + $not: { + $elemMatch: { + v: { $gte: 5 }, + }, }, - { - level: 'L2', - syllableCount: { $eq: 4 }, - contentType: 'Word', + }, + contentType: 'Sentence', + }, + { + level: 'L3', + wordCount: { $lte: 10 }, + contentType: 'Paragraph', + }, + { + level: 'L4', + wordCount: { $gt: 5, $lte: 7 }, + syllableCount: { $lte: 20 }, + syllableCountArray: { + $not: { + $elemMatch: { + v: { $gte: 7 }, + }, }, - { - level: 'L2', - wordCount: { $gte: 2, $lte: 3 }, - syllableCount: { $lte: 8 }, - syllableCountArray: { - $not: { - $elemMatch: { - v: { $gte: 4 }, - }, - }, - }, - contentType: 'Sentence', - }, - { - level: 'L3', - syllableCount: { $gt: 4 }, - contentType: 'Word', - }, - { - level: 'L3', - wordCount: { $gt: 2, $lte: 5 }, - syllableCount: { $lte: 15 }, - syllableCountArray: { - $not: { - $elemMatch: { - v: { $gte: 5 }, - }, - }, - }, - contentType: 'Sentence', - }, - { - level: 'L3', - wordCount: { $lte: 10 }, - contentType: 'Paragraph', - }, - { - level: 'L4', - wordCount: { $gt: 5, $lte: 7 }, - syllableCount: { $lte: 20 }, - syllableCountArray: { - $not: { - $elemMatch: { - v: { $gte: 7 }, - }, - }, - }, - contentType: 'Sentence', - }, - { - level: 'L4', - wordCount: { $lte: 10 }, - contentType: 'Paragraph', - }, - { - level: 'L5', - wordCount: { $gte: 7, $lte: 10 }, - contentType: 'Sentence', - }, - { - level: 'L5', - wordCount: { $gt: 10, $lte: 15 }, - contentType: 'Paragraph', - }, - { - level: 'L6', - wordCount: { $gte: 7, $lte: 12 }, - contentType: 'Sentence', - }, - { - level: 'L6', - wordCount: { $gt: 15 }, - contentType: 'Paragraph', - }, - ] + }, + contentType: 'Sentence', + }, + { + level: 'L4', + wordCount: { $lte: 10 }, + contentType: 'Paragraph', + }, + { + level: 'L5', + wordCount: { $gte: 7, $lte: 10 }, + contentType: 'Sentence', + }, + { + level: 'L5', + wordCount: { $gt: 10, $lte: 15 }, + contentType: 'Paragraph', + }, + { + level: 'L6', + wordCount: { $gte: 7, $lte: 12 }, + contentType: 'Sentence', + }, + { + level: 'L6', + wordCount: { $gt: 15 }, + contentType: 'Paragraph', + }, + ], }; -export default en_config; \ No newline at end of file +export default en_config; diff --git a/src/config/language/hi.ts b/src/config/language/hi.ts index b5cef56..2a480f2 100644 --- a/src/config/language/hi.ts +++ b/src/config/language/hi.ts @@ -1,7 +1,7 @@ var hi_config = { - language_code: "hi", - contentLevel: [], - complexity: [] + language_code: 'hi', + contentLevel: [], + complexity: [], }; -export default hi_config; \ No newline at end of file +export default hi_config; diff --git a/src/config/language/kn.ts b/src/config/language/kn.ts index 85394b6..cedfed9 100644 --- a/src/config/language/kn.ts +++ b/src/config/language/kn.ts @@ -1,7 +1,7 @@ var kn_config = { - language_code: "kn", - contentLevel: [], - complexity: [] + language_code: 'kn', + contentLevel: [], + complexity: [], }; -export default kn_config; \ No newline at end of file +export default kn_config; diff --git a/src/config/language/ta.ts b/src/config/language/ta.ts index e8357b7..a27dd75 100644 --- a/src/config/language/ta.ts +++ b/src/config/language/ta.ts @@ -1,7 +1,7 @@ var ta_config = { - language_code: "ta", - contentLevel: [], - complexity: [] + language_code: 'ta', + contentLevel: [], + complexity: [], }; -export default ta_config; \ No newline at end of file +export default ta_config; diff --git a/src/config/language/te.ts b/src/config/language/te.ts index 880a8b9..3e516ec 100644 --- a/src/config/language/te.ts +++ b/src/config/language/te.ts @@ -1,7 +1,7 @@ var te_config = { - language_code: "te", - contentLevel: [], - complexity: [] + language_code: 'te', + contentLevel: [], + complexity: [], }; -export default te_config; \ No newline at end of file +export default te_config; diff --git a/src/controllers/collection.controller.ts b/src/controllers/collection.controller.ts index 04b4be0..e8c806b 100644 --- a/src/controllers/collection.controller.ts +++ b/src/controllers/collection.controller.ts @@ -8,7 +8,7 @@ import { Post, Put, Res, - UseGuards + UseGuards, } from '@nestjs/common'; import { collection } from 'src/schemas/collection.schema'; import { CollectionService } from 'src/services/collection.service'; @@ -28,9 +28,8 @@ import { JwtAuthGuard } from 'src/auth/auth.guard'; @Controller('collection') @UseGuards(JwtAuthGuard) export class CollectionController { - constructor(private readonly CollectionService: CollectionService) { } + constructor(private readonly CollectionService: CollectionService) {} - @ApiBody({ description: 'Request body for storing data to collection', schema: { @@ -66,7 +65,10 @@ export class CollectionController { createdAt: { type: 'string', example: '2024-06-07T06:14:44.161Z' }, updatedAt: { type: 'string', example: '2024-06-07T06:14:44.161Z' }, _id: { type: 'string', example: '6662a5848946f51e15abb9fd' }, - collectionId: { type: 'string', example: '7b762891-8337-46a6-8eb0-abfcdc5c7f35' }, + collectionId: { + type: 'string', + example: '7b762891-8337-46a6-8eb0-abfcdc5c7f35', + }, __v: { type: 'number', example: 0 }, }, }, @@ -105,7 +107,6 @@ export class CollectionController { } } - @ApiResponse({ status: 200, description: 'Successfully retrieved items', @@ -126,9 +127,18 @@ export class CollectionController { language: { type: 'string', example: 'kn' }, status: { type: 'string', example: 'live' }, tags: { type: 'array', items: { type: 'string' }, example: [] }, - createdAt: { type: 'string', example: '2024-06-04T11:07:02.300Z' }, - updatedAt: { type: 'string', example: '2024-06-04T11:07:02.300Z' }, - collectionId: { type: 'string', example: '58009c39-fd86-45a5-bc32-9638a8198521' }, + createdAt: { + type: 'string', + example: '2024-06-04T11:07:02.300Z', + }, + updatedAt: { + type: 'string', + example: '2024-06-04T11:07:02.300Z', + }, + collectionId: { + type: 'string', + example: '58009c39-fd86-45a5-bc32-9638a8198521', + }, __v: { type: 'number', example: 0 }, }, }, @@ -148,7 +158,7 @@ export class CollectionController { }, }) @ApiOperation({ - summary: 'Get all data from the collection' + summary: 'Get all data from the collection', }) @Get() async fatchAll(@Res() response: FastifyReply) { @@ -162,7 +172,6 @@ export class CollectionController { }); } } - @ApiParam({ name: 'language', @@ -170,7 +179,8 @@ export class CollectionController { }) @ApiResponse({ status: 200, - description: 'Successfully retrieved the collection data for the selected language', + description: + 'Successfully retrieved the collection data for the selected language', schema: { type: 'object', properties: { @@ -188,9 +198,18 @@ export class CollectionController { language: { type: 'string', example: 'kn' }, status: { type: 'string', example: 'live' }, tags: { type: 'array', items: { type: 'string' }, example: [] }, - createdAt: { type: 'string', example: '2024-06-04T11:07:02.300Z' }, - updatedAt: { type: 'string', example: '2024-06-04T11:07:02.300Z' }, - collectionId: { type: 'string', example: '58009c39-fd86-45a5-bc32-9638a8198521' }, + createdAt: { + type: 'string', + example: '2024-06-04T11:07:02.300Z', + }, + updatedAt: { + type: 'string', + example: '2024-06-04T11:07:02.300Z', + }, + collectionId: { + type: 'string', + example: '58009c39-fd86-45a5-bc32-9638a8198521', + }, __v: { type: 'number', example: 0 }, }, }, @@ -210,7 +229,7 @@ export class CollectionController { }, }) @ApiOperation({ - summary: 'Get all data from the collection with the specific language' + summary: 'Get all data from the collection with the specific language', }) @Get('/bylanguage/:language') async fatchByLanguage( @@ -228,7 +247,6 @@ export class CollectionController { } } - @ApiParam({ name: 'id', example: '65717aea18da2cbda941cee2', @@ -249,10 +267,17 @@ export class CollectionController { author: { type: 'string', example: 'Ekstep' }, language: { type: 'string', example: 'kn' }, status: { type: 'string', example: 'live' }, - tags: { type: 'array', items: { type: 'string' }, example: ['ASR'] }, + tags: { + type: 'array', + items: { type: 'string' }, + example: ['ASR'], + }, createdAt: { type: 'string', example: '2024-06-07T06:14:44.161Z' }, updatedAt: { type: 'string', example: '2024-06-07T06:14:44.161Z' }, - collectionId: { type: 'string', example: '7b762891-8337-46a6-8eb0-abfcdc5c7f35' }, + collectionId: { + type: 'string', + example: '7b762891-8337-46a6-8eb0-abfcdc5c7f35', + }, __v: { type: 'number', example: 0 }, }, }, @@ -271,7 +296,7 @@ export class CollectionController { }, }) @ApiOperation({ - summary: 'Get the collection data for collection id' + summary: 'Get the collection data for collection id', }) @Get('/:id') async findById(@Res() response: FastifyReply, @Param('id') id) { @@ -281,7 +306,6 @@ export class CollectionController { }); } - @ApiParam({ name: 'id', example: '65717aea18da2cbda941cee2', @@ -297,10 +321,17 @@ export class CollectionController { author: { type: 'string', example: 'ASER' }, language: { type: 'string', example: 'ta' }, status: { type: 'string', example: 'live' }, - tags: { type: 'array', items: { type: 'string' }, example: ['ASER', 'set1', 'm1'] }, + tags: { + type: 'array', + items: { type: 'string' }, + example: ['ASER', 'set1', 'm1'], + }, createdAt: { type: 'string', example: '2023-12-18T10:53:49.787Z' }, updatedAt: { type: 'string', example: '2023-12-18T10:53:49.788Z' }, - collectionId: { type: 'string', example: '94312c93-5bb8-4144-8822-9a61ad1cd5a8' }, + collectionId: { + type: 'string', + example: '94312c93-5bb8-4144-8822-9a61ad1cd5a8', + }, __v: { type: 'number', example: 0 }, }, }, @@ -321,10 +352,17 @@ export class CollectionController { author: { type: 'string', example: 'Ekstep' }, language: { type: 'string', example: 'kn' }, status: { type: 'string', example: 'live' }, - tags: { type: 'array', items: { type: 'string' }, example: ['ASR'] }, + tags: { + type: 'array', + items: { type: 'string' }, + example: ['ASR'], + }, createdAt: { type: 'string', example: '2024-06-07T06:14:44.161Z' }, updatedAt: { type: 'string', example: '2024-06-07T06:14:44.161Z' }, - collectionId: { type: 'string', example: '7b762891-8337-46a6-8eb0-abfcdc5c7f35' }, + collectionId: { + type: 'string', + example: '7b762891-8337-46a6-8eb0-abfcdc5c7f35', + }, __v: { type: 'number', example: 0 }, }, }, @@ -343,7 +381,7 @@ export class CollectionController { }, }) @ApiOperation({ - summary: 'update the collection data using collection id' + summary: 'update the collection data using collection id', }) @Put('/:id') async update( @@ -357,7 +395,6 @@ export class CollectionController { }); } - @ApiParam({ name: 'id', example: '65717aea18da2cbda941cee2', @@ -378,10 +415,17 @@ export class CollectionController { author: { type: 'string', example: 'ASER' }, language: { type: 'string', example: 'kn' }, status: { type: 'string', example: 'live' }, - tags: { type: 'array', items: { type: 'string' }, example: ['ASER', 'set1', 'm1'] }, + tags: { + type: 'array', + items: { type: 'string' }, + example: ['ASER', 'set1', 'm1'], + }, createdAt: { type: 'string', example: '2023-12-18T10:53:49.787Z' }, updatedAt: { type: 'string', example: '2023-12-18T10:53:49.788Z' }, - collectionId: { type: 'string', example: '94312c93-5bb8-4144-8822-9a61ad1cd5a8' }, + collectionId: { + type: 'string', + example: '94312c93-5bb8-4144-8822-9a61ad1cd5a8', + }, __v: { type: 'number', example: 0 }, }, }, @@ -400,7 +444,7 @@ export class CollectionController { }, }) @ApiOperation({ - summary: 'delete the collection data using collection id' + summary: 'delete the collection data using collection id', }) @Delete('/:id') async delete(@Res() response: FastifyReply, @Param('id') id) { diff --git a/src/controllers/content.controller.ts b/src/controllers/content.controller.ts index 23895ae..222b29a 100644 --- a/src/controllers/content.controller.ts +++ b/src/controllers/content.controller.ts @@ -37,14 +37,17 @@ export class contentController { private readonly contentService: contentService, private readonly collectionService: CollectionService, private readonly httpService: HttpService, - ) { } + ) {} @ApiBody({ description: 'Request body for storing the data into the content', schema: { type: 'object', properties: { - collectionId: { type: 'string', example: '3f0192af-0720-4248-b4d4-d99a9f731d4f' }, + collectionId: { + type: 'string', + example: '3f0192af-0720-4248-b4d4-d99a9f731d4f', + }, name: { type: 'string', example: 'tn gr2 eng t1 ch2d' }, contentType: { type: 'string', example: 'Sentence' }, contentSourceData: { @@ -54,7 +57,10 @@ export class contentController { properties: { language: { type: 'string', example: 'en' }, audioUrl: { type: 'string', example: '' }, - text: { type: 'string', example: 'Blue bird, blue bird, what do you see?' }, + text: { + type: 'string', + example: 'Blue bird, blue bird, what do you see?', + }, }, }, }, @@ -77,7 +83,10 @@ export class contentController { data: { type: 'object', properties: { - collectionId: { type: 'string', example: '3f0192af-0720-4248-b4d4-d99a9f731d4f' }, + collectionId: { + type: 'string', + example: '3f0192af-0720-4248-b4d4-d99a9f731d4f', + }, name: { type: 'string', example: 'tn gr2 eng t1 ch2d' }, contentType: { type: 'string', example: 'Sentence' }, imagePath: { type: 'string', example: 'image_2.jpg' }, @@ -88,8 +97,42 @@ export class contentController { properties: { language: { type: 'string', example: 'en' }, audioUrl: { type: 'string', example: '' }, - text: { type: 'string', example: 'Blue bird, blue bird, what do you see?' }, - phonemes: { type: 'array', items: { type: 'string' }, example: ['b', 'l', 'u', 'b', 'ə', 'r', 'd', ',', 'b', 'l', 'u', 'b', 'ə', 'r', 'd', ',', 'w', 'ə', 't', 'd', 'u', 'j', 'u', 's', 'i', '?'] }, + text: { + type: 'string', + example: 'Blue bird, blue bird, what do you see?', + }, + phonemes: { + type: 'array', + items: { type: 'string' }, + example: [ + 'b', + 'l', + 'u', + 'b', + 'ə', + 'r', + 'd', + ',', + 'b', + 'l', + 'u', + 'b', + 'ə', + 'r', + 'd', + ',', + 'w', + 'ə', + 't', + 'd', + 'u', + 'j', + 'u', + 's', + 'i', + '?', + ], + }, wordCount: { type: 'number', example: 8 }, wordFrequency: { type: 'object', @@ -99,8 +142,8 @@ export class contentController { what: 1, do: 1, you: 1, - see: 1 - } + see: 1, + }, }, syllableCount: { type: 'number', example: 28 }, syllableCountMap: { @@ -111,11 +154,11 @@ export class contentController { what: 4, do: 2, you: 3, - see: 3 - } - } - } - } + see: 3, + }, + }, + }, + }, }, status: { type: 'string', example: 'live' }, publisher: { type: 'string', example: 'ekstep' }, @@ -125,12 +168,15 @@ export class contentController { createdAt: { type: 'string', example: '2024-06-07T09:48:00.040Z' }, updatedAt: { type: 'string', example: '2024-06-07T09:48:00.040Z' }, _id: { type: 'string', example: '6662d7ff059b133df04db6e3' }, - contentId: { type: 'string', example: 'fa853c29-bf19-417a-9661-c67d2671ebc1' }, - __v: { type: 'number', example: 0 } - } - } - } - } + contentId: { + type: 'string', + example: 'fa853c29-bf19-417a-9661-c67d2671ebc1', + }, + __v: { type: 'number', example: 0 }, + }, + }, + }, + }, }) @ApiResponse({ status: 500, @@ -145,8 +191,7 @@ export class contentController { }) @ApiForbiddenResponse({ description: 'Forbidden.' }) @ApiOperation({ - summary: - 'Store the data into to the content table', + summary: 'Store the data into to the content table', }) @Post() async create(@Res() response: FastifyReply, @Body() content: any) { @@ -380,13 +425,18 @@ export class contentController { type: { type: 'string', description: 'content type', example: 'word' }, page: { type: 'number', description: 'Page number', example: 1 }, limit: { type: 'number', description: 'Items per page', example: 10 }, - collectionId: { type: 'string', description: 'ID of the collection', example: '3f0192af-0720-4248-b4d4-d99a9f731d4f' } - } - } + collectionId: { + type: 'string', + description: 'ID of the collection', + example: '3f0192af-0720-4248-b4d4-d99a9f731d4f', + }, + }, + }, }) @ApiResponse({ status: 200, - description: 'The content is search by using the collection id limit and page criteria', + description: + 'The content is search by using the collection id limit and page criteria', schema: { type: 'object', properties: { @@ -403,23 +453,32 @@ export class contentController { items: { type: 'object', properties: { - text: { type: 'string', example: 'Blue bird, blue bird, what do you see?' }, + text: { + type: 'string', + example: 'Blue bird, blue bird, what do you see?', + }, phonemes: { type: 'array', - items: { type: 'string', example: ['b', 'l', 'u', 'b', 'ə', 'r'] } + items: { + type: 'string', + example: ['b', 'l', 'u', 'b', 'ə', 'r'], + }, }, - syllableCount: { type: 'number', example: 28 } - } - } + syllableCount: { type: 'number', example: 28 }, + }, + }, }, language: { type: 'string', example: 'en' }, - contentId: { type: 'string', example: 'fa853c29-bf19-417a-9661-c67d2671ebc1' } - } - } + contentId: { + type: 'string', + example: 'fa853c29-bf19-417a-9661-c67d2671ebc1', + }, + }, + }, }, - totalSyllableCount: { type: 'number', example: 26 } - } - } + totalSyllableCount: { type: 'number', example: 26 }, + }, + }, }) @ApiResponse({ status: 500, @@ -493,9 +552,9 @@ export class contentController { properties: { type: { type: 'string', description: 'content type', example: 'word' }, language: { type: 'number', description: 'Page number', example: 1 }, - limit: { type: 'number', description: 'Items per page', example: 10 } - } - } + limit: { type: 'number', description: 'Items per page', example: 10 }, + }, + }, }) @ApiResponse({ status: 200, @@ -510,7 +569,10 @@ export class contentController { type: 'object', properties: { _id: { type: 'string', example: '6662d7ff059b133df04db6e3' }, - collectionId: { type: 'string', example: '3f0192af-0720-4248-b4d4-d99a9f731d4f' }, + collectionId: { + type: 'string', + example: '3f0192af-0720-4248-b4d4-d99a9f731d4f', + }, name: { type: 'string', example: 'tn gr2 eng t1 ch2d' }, contentType: { type: 'string', example: 'Sentence' }, imagePath: { type: 'string', example: 'image_2.jpg' }, @@ -521,10 +583,43 @@ export class contentController { properties: { language: { type: 'string', example: 'en' }, audioUrl: { type: 'string', example: '' }, - text: { type: 'string', example: 'Blue bird, blue bird, what do you see?' }, + text: { + type: 'string', + example: 'Blue bird, blue bird, what do you see?', + }, phonemes: { type: 'array', - items: { type: 'string', example: ['b', 'l', 'u', 'b', 'ə', 'r', 'd', ',', 'b', 'l', 'u', 'b', 'ə', 'r', 'd', ',', 'w', 'ə', 't', 'd', 'u', 'j', 'u', 's', 'i', '?'] } + items: { + type: 'string', + example: [ + 'b', + 'l', + 'u', + 'b', + 'ə', + 'r', + 'd', + ',', + 'b', + 'l', + 'u', + 'b', + 'ə', + 'r', + 'd', + ',', + 'w', + 'ə', + 't', + 'd', + 'u', + 'j', + 'u', + 's', + 'i', + '?', + ], + }, }, wordCount: { type: 'number', example: 8 }, wordFrequency: { @@ -535,8 +630,8 @@ export class contentController { what: 1, do: 1, you: 1, - see: 1 - } + see: 1, + }, }, syllableCount: { type: 'number', example: 28 }, syllableCountMap: { @@ -547,26 +642,35 @@ export class contentController { what: 4, do: 2, you: 3, - see: 3 - } - } - } - } + see: 3, + }, + }, + }, + }, }, status: { type: 'string', example: 'live' }, publisher: { type: 'string', example: 'ekstep' }, language: { type: 'string', example: 'en' }, contentIndex: { type: 'number', example: 1 }, tags: { type: 'array', items: { type: 'string' }, example: [] }, - createdAt: { type: 'string', example: '2024-06-07T09:48:00.040Z' }, - updatedAt: { type: 'string', example: '2024-06-07T09:48:00.040Z' }, - contentId: { type: 'string', example: 'fa853c29-bf19-417a-9661-c67d2671ebc1' }, - __v: { type: 'number', example: 0 } - } - } - } - } - } + createdAt: { + type: 'string', + example: '2024-06-07T09:48:00.040Z', + }, + updatedAt: { + type: 'string', + example: '2024-06-07T09:48:00.040Z', + }, + contentId: { + type: 'string', + example: 'fa853c29-bf19-417a-9661-c67d2671ebc1', + }, + __v: { type: 'number', example: 0 }, + }, + }, + }, + }, + }, }) @ApiResponse({ status: 500, @@ -679,36 +783,36 @@ export class contentController { description: 'Array of tokens', items: { type: 'string', - example: 'c' - } + example: 'c', + }, }, language: { type: 'string', description: 'Language code', - example: 'en' + example: 'en', }, contentType: { type: 'string', description: 'Type of content', - example: 'Word' + example: 'Word', }, limit: { type: 'number', description: 'Limit on the number of items', - example: 5 + example: 5, }, cLevel: { type: 'string', description: 'Content level', - example: 'L2' + example: 'L2', }, complexityLevel: { type: 'array', description: 'Array of complexity levels', items: { type: 'string', - example: 'C1' - } + example: 'C1', + }, }, graphemesMappedObj: { type: 'object', @@ -717,23 +821,23 @@ export class contentController { type: 'array', items: { type: 'string', - example: 'ch' - } + example: 'ch', + }, }, example: { - "c": ["ch"], - "o": ["o"], - "a": ["a"], - "v": ["v", "ve"], - "w": ["w", "wh"], - "æ": ["a", "ai", "au"], - "n": ["n"], - "i": ["i"], - "θ": ["th"] - } - } - } - } + c: ['ch'], + o: ['o'], + a: ['a'], + v: ['v', 've'], + w: ['w', 'wh'], + æ: ['a', 'ai', 'au'], + n: ['n'], + i: ['i'], + θ: ['th'], + }, + }, + }, + }, }) @ApiResponse({ status: 200, @@ -751,8 +855,14 @@ export class contentController { type: 'object', properties: { _id: { type: 'string', example: '660f9545367a62b3902dd58b' }, - contentId: { type: 'string', example: 'f8dd7c97-53f7-4676-b597-4a52aaface5c' }, - collectionId: { type: 'string', example: '6a519951-8635-4d89-821a-d3eb60f6e1ec' }, + contentId: { + type: 'string', + example: 'f8dd7c97-53f7-4676-b597-4a52aaface5c', + }, + collectionId: { + type: 'string', + example: '6a519951-8635-4d89-821a-d3eb60f6e1ec', + }, name: { type: 'string', example: 'L2_new_3' }, contentType: { type: 'string', example: 'Word' }, contentSourceData: { @@ -765,17 +875,17 @@ export class contentController { text: { type: 'string', example: 'five' }, phonemes: { type: 'array', - items: { type: 'string', example: 'f' } + items: { type: 'string', example: 'f' }, }, wordCount: { type: 'number', example: 1 }, wordFrequency: { type: 'object', - additionalProperties: { type: 'number', example: 1 } + additionalProperties: { type: 'number', example: 1 }, }, syllableCount: { type: 'number', example: 4 }, syllableCountMap: { type: 'object', - additionalProperties: { type: 'number', example: 4 } + additionalProperties: { type: 'number', example: 4 }, }, syllableCountArray: { type: 'array', @@ -783,12 +893,12 @@ export class contentController { type: 'object', properties: { k: { type: 'string', example: 'five' }, - v: { type: 'number', example: 4 } - } - } - } - } - } + v: { type: 'number', example: 4 }, + }, + }, + }, + }, + }, }, status: { type: 'string', example: 'live' }, publisher: { type: 'string', example: 'ekstep' }, @@ -796,17 +906,23 @@ export class contentController { contentIndex: { type: 'number', example: 141 }, tags: { type: 'array', - items: { type: 'string' } + items: { type: 'string' }, + }, + createdAt: { + type: 'string', + example: '2024-04-05T05:45:55.335Z', + }, + updatedAt: { + type: 'string', + example: '2024-04-05T05:45:55.335Z', }, - createdAt: { type: 'string', example: '2024-04-05T05:45:55.335Z' }, - updatedAt: { type: 'string', example: '2024-04-05T05:45:55.335Z' }, __v: { type: 'number', example: 0 }, matchedChar: { type: 'array', - items: { type: 'string', example: 'v' } - } - } - } + items: { type: 'string', example: 'v' }, + }, + }, + }, }, contentForToken: { type: 'object', @@ -815,9 +931,18 @@ export class contentController { items: { type: 'object', properties: { - _id: { type: 'string', example: '660f9545367a62b3902dd58b' }, - contentId: { type: 'string', example: 'f8dd7c97-53f7-4676-b597-4a52aaface5c' }, - collectionId: { type: 'string', example: '6a519951-8635-4d89-821a-d3eb60f6e1ec' }, + _id: { + type: 'string', + example: '660f9545367a62b3902dd58b', + }, + contentId: { + type: 'string', + example: 'f8dd7c97-53f7-4676-b597-4a52aaface5c', + }, + collectionId: { + type: 'string', + example: '6a519951-8635-4d89-821a-d3eb60f6e1ec', + }, name: { type: 'string', example: 'L2_new_3' }, contentType: { type: 'string', example: 'Word' }, contentSourceData: { @@ -830,17 +955,23 @@ export class contentController { text: { type: 'string', example: 'five' }, phonemes: { type: 'array', - items: { type: 'string', example: 'f' } + items: { type: 'string', example: 'f' }, }, wordCount: { type: 'number', example: 1 }, wordFrequency: { type: 'object', - additionalProperties: { type: 'number', example: 1 } + additionalProperties: { + type: 'number', + example: 1, + }, }, syllableCount: { type: 'number', example: 4 }, syllableCountMap: { type: 'object', - additionalProperties: { type: 'number', example: 4 } + additionalProperties: { + type: 'number', + example: 4, + }, }, syllableCountArray: { type: 'array', @@ -848,12 +979,12 @@ export class contentController { type: 'object', properties: { k: { type: 'string', example: 'five' }, - v: { type: 'number', example: 4 } - } - } - } - } - } + v: { type: 'number', example: 4 }, + }, + }, + }, + }, + }, }, status: { type: 'string', example: 'live' }, publisher: { type: 'string', example: 'ekstep' }, @@ -861,23 +992,29 @@ export class contentController { contentIndex: { type: 'number', example: 141 }, tags: { type: 'array', - items: { type: 'string' } + items: { type: 'string' }, + }, + createdAt: { + type: 'string', + example: '2024-04-05T05:45:55.335Z', + }, + updatedAt: { + type: 'string', + example: '2024-04-05T05:45:55.335Z', }, - createdAt: { type: 'string', example: '2024-04-05T05:45:55.335Z' }, - updatedAt: { type: 'string', example: '2024-04-05T05:45:55.335Z' }, __v: { type: 'number', example: 0 }, matchedChar: { type: 'array', - items: { type: 'string', example: 'v' } - } - } - } - } - } - } - } - } - } + items: { type: 'string', example: 'v' }, + }, + }, + }, + }, + }, + }, + }, + }, + }, }) @ApiResponse({ status: 500, @@ -891,7 +1028,7 @@ export class contentController { }, }) @ApiOperation({ - summary: 'Get all data from the content table' + summary: 'Get all data from the content table', }) @Post('/getContent') async getContent(@Res() response: FastifyReply, @Body() queryData: any) { @@ -901,44 +1038,58 @@ export class contentController { let contentCollection; let collectionId; - if(queryData.story_mode === "true" && queryData.level_competency.length > 0) { - collectionId = await this.collectionService.getCompetencyCollections(queryData.level_competency, queryData.language, queryData.contentType); - const contentData = await this.contentService.pagination(0, parseInt(Batch),queryData.contentType,collectionId); + if ( + queryData.story_mode === 'true' && + queryData.level_competency.length > 0 + ) { + collectionId = await this.collectionService.getCompetencyCollections( + queryData.level_competency, + queryData.language, + queryData.contentType, + ); + const contentData = await this.contentService.pagination( + 0, + parseInt(Batch), + queryData.contentType, + collectionId, + ); let contentArr = contentData['data']; - if(contentArr.length === 0){ - - await this.contentService.search( - queryData.tokenArr, - queryData.language, - queryData.contentType, - parseInt(Batch), - queryData.tags, - queryData.cLevel, - queryData.complexityLevel, - queryData.graphemesMappedObj, - queryData.level_competency - ).then((contentData)=>{ - contentArr = contentData['wordsArr']; - }); + if (contentArr.length === 0) { + await this.contentService + .search( + queryData.tokenArr, + queryData.language, + queryData.contentType, + parseInt(Batch), + queryData.tags, + queryData.cLevel, + queryData.complexityLevel, + queryData.graphemesMappedObj, + queryData.level_competency, + ) + .then((contentData) => { + contentArr = contentData['wordsArr']; + }); } - if(queryData.mechanics_id !== undefined){ + if (queryData.mechanics_id !== undefined) { contentArr.map((content) => { const { mechanics_data } = content; - if(mechanics_data){ - const mechanicData = mechanics_data.find( - (mechanic) => {return mechanic.mechanics_id === queryData.mechanics_id} - ); - content.mechanics_data = []; - content.mechanics_data.push(mechanicData); - }}); + if (mechanics_data) { + const mechanicData = mechanics_data.find((mechanic) => { + return mechanic.mechanics_id === queryData.mechanics_id; + }); + content.mechanics_data = []; + content.mechanics_data.push(mechanicData); + } + }); } - - contentCollection ={ wordsArr:contentArr}; + + contentCollection = { wordsArr: contentArr }; } - if(queryData.mechanics_id === undefined && collectionId === undefined){ + if (queryData.mechanics_id === undefined && collectionId === undefined) { contentCollection = await this.contentService.search( queryData.tokenArr, queryData.language, @@ -948,9 +1099,8 @@ export class contentController { queryData.cLevel, queryData.complexityLevel, queryData.graphemesMappedObj, - queryData.level_competency + queryData.level_competency, ); - } else { contentCollection = await this.contentService.getMechanicsContentData( queryData.contentType, @@ -958,7 +1108,7 @@ export class contentController { parseInt(Batch), queryData.language, queryData.level_competency, - queryData.tags + queryData.tags, ); } @@ -977,25 +1127,40 @@ export class contentController { @ApiExcludeEndpoint(true) @Post('/getContentByFilters') - async getContentByFilters(@Res() response: FastifyReply, @Body() queryData: any) { + async getContentByFilters( + @Res() response: FastifyReply, + @Body() queryData: any, + ) { try { let Batch: any = queryData.limit || 5; - const contentCollection = await this.contentService.searchByFilter(queryData?.syllableList, queryData?.syllableCount, queryData?.wordCount, queryData?.totalOrthoComplexity, queryData?.totalPhonicComplexity, queryData?.meanPhonicComplexity, queryData.language, queryData.contentType, parseInt(Batch), queryData?.contentId, queryData?.collectionId, queryData?.tags); + const contentCollection = await this.contentService.searchByFilter( + queryData?.syllableList, + queryData?.syllableCount, + queryData?.wordCount, + queryData?.totalOrthoComplexity, + queryData?.totalPhonicComplexity, + queryData?.meanPhonicComplexity, + queryData.language, + queryData.contentType, + parseInt(Batch), + queryData?.contentId, + queryData?.collectionId, + queryData?.tags, + ); return response.status(HttpStatus.CREATED).send({ - status: "success", + status: 'success', data: contentCollection, }); } catch (error) { console.log(error); return response.status(HttpStatus.INTERNAL_SERVER_ERROR).send({ - status: "error", - message: "Server error - " + error + status: 'error', + message: 'Server error - ' + error, }); } } - @ApiBody({ description: 'Request body parameters', required: true, @@ -1007,16 +1172,16 @@ export class contentController { description: 'Array of tags', items: { type: 'string', - example: 'ASER' - } + example: 'ASER', + }, }, language: { type: 'string', description: 'Language code', - example: 'ta' - } - } - } + example: 'ta', + }, + }, + }, }) @ApiResponse({ status: 200, @@ -1032,13 +1197,16 @@ export class contentController { _id: { type: 'string', example: '65e88b6cdee499a6209e739e' }, name: { type: 'string', example: '(மாதிறி -4)எழுத்து' }, category: { type: 'string', example: 'Char' }, - collectionId: { type: 'string', example: 'ed47eb63-87c8-41f4-821d-1400fef37b78' } - } - } + collectionId: { + type: 'string', + example: 'ed47eb63-87c8-41f4-821d-1400fef37b78', + }, + }, + }, }, - status: { type: 'number', example: 200 } - } - } + status: { type: 'number', example: 200 }, + }, + }, }) @ApiResponse({ status: 500, @@ -1052,37 +1220,43 @@ export class contentController { }, }) @ApiOperation({ - summary: 'Get Assessments data' + summary: 'Get Assessments data', }) @Post('/getAssessment') async getAssessment(@Res() response: FastifyReply, @Body() queryData: any) { try { let contentCollection; - if (queryData.tags.includes("ASER")) { + if (queryData.tags.includes('ASER')) { let collectionArr = []; for (let setno = 1; setno <= 5; setno++) { let tags = []; tags.push(...queryData.tags); - tags.push("set" + setno); - let collection = await this.collectionService.getAssessment(tags, queryData.language); + tags.push('set' + setno); + let collection = await this.collectionService.getAssessment( + tags, + queryData.language, + ); if (collection.data[0] != null) { collectionArr.push(collection.data[0]); } } contentCollection = { data: collectionArr, - status: 200 + status: 200, }; } else { - contentCollection = await this.collectionService.getAssessment(queryData.tags, queryData.language); + contentCollection = await this.collectionService.getAssessment( + queryData.tags, + queryData.language, + ); } return response.status(HttpStatus.CREATED).send(contentCollection); } catch (error) { return response.status(HttpStatus.INTERNAL_SERVER_ERROR).send({ - status: "error", - message: "Server error - " + error + status: 'error', + message: 'Server error - ' + error, }); } } @@ -1113,7 +1287,11 @@ export class contentController { @ApiExcludeEndpoint(true) @Get() - async fetchAll(@Res() response: FastifyReply, @Query('page') page: number = 1, @Query('limit') limit: number = 20) { + async fetchAll( + @Res() response: FastifyReply, + @Query('page') page: number = 1, + @Query('limit') limit: number = 20, + ) { try { const limitCount = limit; const data = await this.contentService.readAll(page, limit); @@ -1123,13 +1301,12 @@ export class contentController { status: 'success', recordCount: dataCount, pageCount: pageCount, - data + data, }); - } catch (error) { return response.status(HttpStatus.INTERNAL_SERVER_ERROR).send({ - status: "error", - message: "Server error - " + error + status: 'error', + message: 'Server error - ' + error, }); } } diff --git a/src/main.ts b/src/main.ts index 4032860..5d7b63b 100644 --- a/src/main.ts +++ b/src/main.ts @@ -8,7 +8,6 @@ import { ValidationPipe } from '@nestjs/common'; import { SwaggerModule, DocumentBuilder } from '@nestjs/swagger'; import { AppClusterService } from './app-cluster.service'; - async function bootstrap() { const app = await NestFactory.create( AppModule, @@ -21,6 +20,11 @@ async function bootstrap() { origin: ['*'], methods: ['GET', 'POST', 'HEAD', 'PUT', 'PATCH', 'DELETE', 'OPTIONS'], credentials: false, + exposedHeaders: [ + 'X-Content-Type-Options', + 'X-Frame-Options', + 'X-XSS-Protection', + ], }); const config = new DocumentBuilder() @@ -35,6 +39,16 @@ async function bootstrap() { const document = SwaggerModule.createDocument(app, config); SwaggerModule.setup('api', app, document); + app + .getHttpAdapter() + .getInstance() + .addHook('onSend', async (request, reply, payload) => { + reply.header('X-Content-Type-Options', 'nosniff'); + reply.header('X-Frame-Options', 'DENY'); + reply.header('Content-Security-Policy', "default-src 'self'"); + return payload; + }); + await app.listen(3008, '0.0.0.0'); } AppClusterService.clusterize(bootstrap); diff --git a/src/schemas/content.schema.ts b/src/schemas/content.schema.ts index 46fe16c..98158c3 100644 --- a/src/schemas/content.schema.ts +++ b/src/schemas/content.schema.ts @@ -1,14 +1,20 @@ import { Prop, Schema, SchemaFactory } from '@nestjs/mongoose'; import { Document, now, Mixed } from 'mongoose'; import { v4 as uuidv4 } from 'uuid'; -import { IsOptional, IsString, IsNumber, IsArray, IsObject} from 'class-validator'; +import { + IsOptional, + IsString, + IsNumber, + IsArray, + IsObject, +} from 'class-validator'; @Schema({ collection: 'content' }) export class content { - @Prop({ default: uuidv4, index:true }) + @Prop({ default: uuidv4, index: true }) contentId: string; - @Prop({ type: String, required: false, index:true}) + @Prop({ type: String, required: false, index: true }) @IsOptional() @IsString() collectionId: string; @@ -18,7 +24,7 @@ export class content { @IsString() name: string; - @Prop({ type: String, required: true, index:true }) + @Prop({ type: String, required: true, index: true }) @IsString() contentType: string; @@ -36,8 +42,8 @@ export class content { mechanics_data: [ { mechanics_id: string; - language:string; - content_body?:string; + language: string; + content_body?: string; jumbled_text?: string; text?: string; audio_url?: string; @@ -49,7 +55,7 @@ export class content { image_url: string; isAns: boolean; side: string; - } + }, ]; hints?: { text: string; @@ -58,9 +64,9 @@ export class content { }; time_limit?: number; correctness?: { - "50%": [string], - } - } + '50%': [string]; + }; + }, ]; @Prop({ type: Object, required: false }) @@ -105,7 +111,7 @@ export class content { @IsString() publisher: string; - @Prop({ type: String, required: true, index:true }) + @Prop({ type: String, required: true, index: true }) @IsString() language: string; @@ -117,7 +123,7 @@ export class content { @Prop({ required: true }) tags: [string]; - @Prop({ default: now(), index:true }) + @Prop({ default: now(), index: true }) createdAt: Date; @Prop({ default: now() }) @@ -130,5 +136,5 @@ export const contentSchema = SchemaFactory.createForClass(content); contentSchema.index({ contentType: 1, - "contentSourceData.language": 1 -}); \ No newline at end of file + 'contentSourceData.language': 1, +}); diff --git a/src/services/collection.service.ts b/src/services/collection.service.ts index 207debe..e0d1a95 100644 --- a/src/services/collection.service.ts +++ b/src/services/collection.service.ts @@ -8,7 +8,7 @@ export class CollectionService { constructor( @InjectModel(collection.name) private collectionModel: Model, - ) { } + ) {} async create(collection: collection): Promise { try { @@ -56,9 +56,9 @@ export class CollectionService { _id: 1, name: 1, category: 1, - collectionId: 1 - } - } + collectionId: 1, + }, + }, ]); return { data: data, @@ -66,21 +66,25 @@ export class CollectionService { }; } - async getCompetencyCollections(level_competency = [], language = "en", contentType): Promise { + async getCompetencyCollections( + level_competency = [], + language = 'en', + contentType, + ): Promise { let collectionIds = await this.collectionModel.aggregate([ - { - $match: { - "level_complexity.level_competency": {$in:level_competency}, - "language":language, - "category": contentType - } + { + $match: { + 'level_complexity.level_competency': { $in: level_competency }, + language: language, + category: contentType, + }, }, - { - $sample: { size: 1 } + { + $sample: { size: 1 }, + }, + { + $project: { collectionId: 1, _id: 0 }, }, - { - $project: { collectionId: 1, _id: 0 } - } ]); return collectionIds[0]?.collectionId; @@ -110,4 +114,3 @@ export class CollectionService { return collectionIds[0]?.collectionId; } } - diff --git a/src/services/content.service.ts b/src/services/content.service.ts index bfce36e..716c6be 100644 --- a/src/services/content.service.ts +++ b/src/services/content.service.ts @@ -11,7 +11,7 @@ export class contentService { constructor( @InjectModel(content.name) private content: Model, private readonly httpService: HttpService, - ) { } + ) {} async create(content: content): Promise { try { @@ -32,10 +32,10 @@ export class contentService { return await this.content.countDocuments().exec(); } - async readById(id:any): Promise { + async readById(id: any): Promise { return await this.content.findOne({ contentId: id }).exec(); } - + async update(id, content: content): Promise { return await this.content.findByIdAndUpdate(id, content, { new: true }); } @@ -47,36 +47,38 @@ export class contentService { async pagination(skip, limit, type, collectionId) { const limitValue = parseInt(limit); const skipValue = parseInt(skip); - const data = await this.content.aggregate([ - { - $match: { - collectionId: collectionId - } - }, - { - $project: { - _id: 1, - contentType: 1, - contentId: 1, - language: 1, - "contentSourceData.text": 1, - "contentSourceData.phonemes": 1, - "contentSourceData.syllableCount": 1, - "contentSourceData.hallucination_alternative": 1, - "mechanics_data":1, - "contentIndex":1 - } - }, - { - $skip: skipValue - }, - { - $limit: limitValue - }, - { - $sort: { contentIndex: 1 } - } - ]).exec(); + const data = await this.content + .aggregate([ + { + $match: { + collectionId: collectionId, + }, + }, + { + $project: { + _id: 1, + contentType: 1, + contentId: 1, + language: 1, + 'contentSourceData.text': 1, + 'contentSourceData.phonemes': 1, + 'contentSourceData.syllableCount': 1, + 'contentSourceData.hallucination_alternative': 1, + mechanics_data: 1, + contentIndex: 1, + }, + }, + { + $skip: skipValue, + }, + { + $limit: limitValue, + }, + { + $sort: { contentIndex: 1 }, + }, + ]) + .exec(); return { data: data, status: 200, @@ -329,10 +331,10 @@ export class contentService { cLevel, complexityLevel, graphemesMappedObj, - level_competency = [] + level_competency = [], ): Promise { - let nextTokenArr = [] - if (tokenArr.length >= (limit * 2)) { + let nextTokenArr = []; + if (tokenArr.length >= limit * 2) { nextTokenArr = tokenArr.slice(limit, limit * 2); } else { nextTokenArr = tokenArr.slice(limit, tokenArr.length); @@ -349,8 +351,8 @@ export class contentService { let complexity = common_config.complexity; if (cLevel != '' || complexityLevel.length != 0) { - if (cLevel != "L1") { - prevContentLevel = "L" + (parseInt(cLevel[1]) - 1); + if (cLevel != 'L1') { + prevContentLevel = 'L' + (parseInt(cLevel[1]) - 1); } contentQueryParam.push( @@ -423,15 +425,14 @@ export class contentService { query = { contentSourceData: { - $elemMatch: { - }, + $elemMatch: {}, }, contentType: contentType, }; if (tokenArr?.length > 0) { query.contentSourceData.$elemMatch.text = { - '$regex': startWithRegexPattern + $regex: startWithRegexPattern, }; } @@ -446,7 +447,7 @@ export class contentService { } if (tags?.length > 0) { - query["tags"] = { $all: tags }; + query['tags'] = { $all: tags }; } let contentDataSet = new Set(); @@ -481,7 +482,10 @@ export class contentService { .exec() .then((doc) => { for (const docEle of doc) { - if (contentData.length == 0 || !contentDataSet.has(docEle.contentId)) { + if ( + contentData.length == 0 || + !contentDataSet.has(docEle.contentId) + ) { contentDataSet.add(docEle.contentId); contentData.push(docEle); } @@ -492,7 +496,7 @@ export class contentService { if (tokenArr?.length > 0) { query.contentSourceData.$elemMatch.text = { - '$regex': inBetweenRegexPattern + $regex: inBetweenRegexPattern, }; } @@ -526,7 +530,10 @@ export class contentService { .exec() .then((doc) => { for (const docEle of doc) { - if (contentData.length == 0 || !contentDataSet.has(docEle.contentId)) { + if ( + contentData.length == 0 || + !contentDataSet.has(docEle.contentId) + ) { contentDataSet.add(docEle.contentId); contentData.push(docEle); } @@ -535,7 +542,6 @@ export class contentService { // Add more targets tokens for content if (contentData.length < limit) { - tokenArr.concat(nextTokenArr); const searchChar = tokenArr.join('|'); @@ -552,7 +558,8 @@ export class contentService { const allCharRegexPattern = new RegExp(`\\B(${searchChar})`, 'gu'); if (tokenArr?.length > 0) { - query.contentSourceData.$elemMatch['text']['$regex'] = allCharRegexPattern; + query.contentSourceData.$elemMatch['text']['$regex'] = + allCharRegexPattern; } await this.content @@ -585,7 +592,10 @@ export class contentService { .exec() .then((doc) => { for (const docEle of doc) { - if (contentData.length == 0 || !contentDataSet.has(docEle.contentId)) { + if ( + contentData.length == 0 || + !contentDataSet.has(docEle.contentId) + ) { contentDataSet.add(docEle.contentId); contentData.push(docEle); } @@ -594,14 +604,16 @@ export class contentService { } // Remove Ortho complexity - if (contentData.length < limit && contentType.toLocaleLowerCase() !== 'paragraph') { - + if ( + contentData.length < limit && + contentType.toLocaleLowerCase() !== 'paragraph' + ) { mileStoneQuery = mileStoneQuery.filter((mileStoneQueryEle) => { return !mileStoneQueryEle.hasOwnProperty('totalOrthoComplexity'); }); - if(mileStoneQuery != undefined || mileStoneQuery.length != 0) { - query.contentSourceData.$elemMatch['$or'] = mileStoneQuery; + if (mileStoneQuery != undefined || mileStoneQuery.length != 0) { + query.contentSourceData.$elemMatch['$or'] = mileStoneQuery; } await this.content @@ -634,17 +646,23 @@ export class contentService { .exec() .then((doc) => { for (const docEle of doc) { - if (contentData.length == 0 || !contentDataSet.has(docEle.contentId)) { + if ( + contentData.length == 0 || + !contentDataSet.has(docEle.contentId) + ) { contentDataSet.add(docEle.contentId); contentData.push(docEle); } } }); - } + } // Remove Phonic complexity - if (contentData.length < limit && contentType.toLocaleLowerCase() !== 'paragraph') { - delete query.contentSourceData.$elemMatch['$or'] + if ( + contentData.length < limit && + contentType.toLocaleLowerCase() !== 'paragraph' + ) { + delete query.contentSourceData.$elemMatch['$or']; await this.content .aggregate([ @@ -676,7 +694,10 @@ export class contentService { .exec() .then((doc) => { for (const docEle of doc) { - if (contentData.length == 0 || !contentDataSet.has(docEle.contentId)) { + if ( + contentData.length == 0 || + !contentDataSet.has(docEle.contentId) + ) { contentDataSet.add(docEle.contentId); contentData.push(docEle); } @@ -739,7 +760,10 @@ export class contentService { .exec() .then((doc) => { for (const docEle of doc) { - if (contentData.length == 0 || !contentDataSet.has(docEle.contentId)) { + if ( + contentData.length == 0 || + !contentDataSet.has(docEle.contentId) + ) { contentDataSet.add(docEle.contentId); contentData.push(docEle); } @@ -749,7 +773,6 @@ export class contentService { // Remove tokens if (contentData.length < limit && tokenArr?.length > 0) { - delete query.contentSourceData.$elemMatch['text']; await this.content @@ -782,7 +805,10 @@ export class contentService { .exec() .then((doc) => { for (const docEle of doc) { - if (contentData.length == 0 || !contentDataSet.has(docEle.contentId)) { + if ( + contentData.length == 0 || + !contentDataSet.has(docEle.contentId) + ) { contentDataSet.add(docEle.contentId); contentData.push(docEle); } @@ -794,52 +820,53 @@ export class contentService { if (contentData.length <= limit) { let randomContentQuery = { contentSourceData: { - $elemMatch: { - }, + $elemMatch: {}, }, - contentType: contentType + contentType: contentType, }; - + randomContentQuery.contentSourceData.$elemMatch['language'] = language; - + await this.content - .aggregate([ - { - $addFields: { - contentSourceData: { - $map: { - input: '$contentSourceData', - as: 'elem', - in: { - $mergeObjects: [ - '$$elem', - { - syllableCountArray: { - $objectToArray: '$$elem.syllableCountMap', + .aggregate([ + { + $addFields: { + contentSourceData: { + $map: { + input: '$contentSourceData', + as: 'elem', + in: { + $mergeObjects: [ + '$$elem', + { + syllableCountArray: { + $objectToArray: '$$elem.syllableCountMap', + }, }, - }, - ], + ], + }, }, }, }, }, - }, - { - $match: randomContentQuery, - }, - { $sample: { size: limit - contentData.length } }, - ]) - .exec() - .then((doc) => { - for (const docEle of doc) { - if (contentData.length == 0 || !contentDataSet.has(docEle.contentId)) { - contentDataSet.add(docEle.contentId); - contentData.push(docEle); + { + $match: randomContentQuery, + }, + { $sample: { size: limit - contentData.length } }, + ]) + .exec() + .then((doc) => { + for (const docEle of doc) { + if ( + contentData.length == 0 || + !contentDataSet.has(docEle.contentId) + ) { + contentDataSet.add(docEle.contentId); + contentData.push(docEle); + } } - } - }); - } - + }); + } for (let contentDataEle of contentData) { const regexMatchBegin = new RegExp( @@ -952,7 +979,7 @@ export class contentService { for (const tokenArrEle of tokenArr) { const contentForTokenArr = []; for (const wordsArrEle of wordsArr) { - if (wordsArrEle.matchedChar && tokenArr?.length > 0){ + if (wordsArrEle.matchedChar && tokenArr?.length > 0) { for (const matchedCharEle of wordsArrEle.matchedChar) { if ( matchedCharEle.match(new RegExp(`(${tokenArrEle})`, 'gu')) != @@ -964,7 +991,11 @@ export class contentService { } } - if (contentForTokenArr.length === 0 && contentType !== 'char' && tokenArr?.length > 0) { + if ( + contentForTokenArr.length === 0 && + contentType !== 'char' && + tokenArr?.length > 0 + ) { query.contentSourceData.$elemMatch.text = new RegExp( `(${tokenArrEle})`, 'gu', @@ -1029,19 +1060,22 @@ export class contentService { } if (cLevel != '') { - if (cLevel != "L1") { - prevContentLevel = "L" + (parseInt(cLevel[1]) - 1); + if (cLevel != 'L1') { + prevContentLevel = 'L' + (parseInt(cLevel[1]) - 1); } for (let contentLevelEle of contentLevel) { - if (contentLevelEle.level === cLevel && - contentLevelEle.contentType === contentType) { + if ( + contentLevelEle.level === cLevel && + contentLevelEle.contentType === contentType + ) { let contentLevelObj = {}; if (contentLevelEle.hasOwnProperty('syllableCount')) { contentLevelObj['syllableCount'] = contentLevelEle.syllableCount; } if (contentLevelEle.hasOwnProperty('syllableCountArray')) { - contentLevelObj['syllableCountArray'] = contentLevelEle.syllableCountArray; + contentLevelObj['syllableCountArray'] = + contentLevelEle.syllableCountArray; } if (contentLevelEle.hasOwnProperty('wordCount')) { contentLevelObj['wordCount'] = contentLevelEle.wordCount; @@ -1053,8 +1087,7 @@ export class contentService { let query = { contentSourceData: { - $elemMatch: { - }, + $elemMatch: {}, }, contentType: contentType, }; @@ -1070,11 +1103,11 @@ export class contentService { } if (tags?.length > 0) { - query["tags"] = { $all: tags }; + query['tags'] = { $all: tags }; } if (level_competency?.length > 0) { - query["level_complexity.level_competency"] = {$in:level_competency}; + query['level_complexity.level_competency'] = { $in: level_competency }; } const allTokenGraphemes = []; @@ -1113,7 +1146,10 @@ export class contentService { .exec() .then((doc) => { for (const docEle of doc) { - if (contentData.length == 0 || !contentDataSet.has(docEle.contentId)) { + if ( + contentData.length == 0 || + !contentDataSet.has(docEle.contentId) + ) { contentDataSet.add(docEle.contentId); contentData.push(docEle); } @@ -1153,7 +1189,10 @@ export class contentService { .exec() .then((doc) => { for (const docEle of doc) { - if (contentData.length == 0 || !contentDataSet.has(docEle.contentId)) { + if ( + contentData.length == 0 || + !contentDataSet.has(docEle.contentId) + ) { contentDataSet.add(docEle.contentId); contentData.push(docEle); } @@ -1166,14 +1205,17 @@ export class contentService { cLevelQuery = []; for (let contentLevelEle of contentLevel) { - if (contentLevelEle.level === prevContentLevel && - contentLevelEle.contentType === contentType) { + if ( + contentLevelEle.level === prevContentLevel && + contentLevelEle.contentType === contentType + ) { let contentLevelObj = {}; if (contentLevelEle.hasOwnProperty('syllableCount')) { contentLevelObj['syllableCount'] = contentLevelEle.syllableCount; } if (contentLevelEle.hasOwnProperty('syllableCountArray')) { - contentLevelObj['syllableCountArray'] = contentLevelEle.syllableCountArray; + contentLevelObj['syllableCountArray'] = + contentLevelEle.syllableCountArray; } if (contentLevelEle.hasOwnProperty('wordCount')) { contentLevelObj['wordCount'] = contentLevelEle.wordCount; @@ -1217,7 +1259,10 @@ export class contentService { .exec() .then((doc) => { for (const docEle of doc) { - if (contentData.length == 0 || !contentDataSet.has(docEle.contentId)) { + if ( + contentData.length == 0 || + !contentDataSet.has(docEle.contentId) + ) { contentDataSet.add(docEle.contentId); contentData.push(docEle); } @@ -1259,7 +1304,10 @@ export class contentService { .exec() .then((doc) => { for (const docEle of doc) { - if (contentData.length == 0 || !contentDataSet.has(docEle.contentId)) { + if ( + contentData.length == 0 || + !contentDataSet.has(docEle.contentId) + ) { contentDataSet.add(docEle.contentId); contentData.push(docEle); } @@ -1270,57 +1318,60 @@ export class contentService { // remove all criteria and get random content with level conpetency if (contentData.length <= limit && level_competency?.length > 0) { let randomContentQuery = { - contentSourceData: { - $elemMatch: { - }, - }, + contentSourceData: { + $elemMatch: {}, + }, contentType: contentType, - "level_complexity.level_competency": {$in:level_competency} + 'level_complexity.level_competency': { $in: level_competency }, }; - - randomContentQuery.contentSourceData.$elemMatch['language'] = en_config.language_code; - + + randomContentQuery.contentSourceData.$elemMatch['language'] = + en_config.language_code; + await this.content - .aggregate([ - { - $addFields: { - contentSourceData: { - $map: { - input: '$contentSourceData', - as: 'elem', - in: { - $mergeObjects: [ - '$$elem', - { - syllableCountArray: { - $objectToArray: '$$elem.syllableCountMap', - }, - }, - ], + .aggregate([ + { + $addFields: { + contentSourceData: { + $map: { + input: '$contentSourceData', + as: 'elem', + in: { + $mergeObjects: [ + '$$elem', + { + syllableCountArray: { + $objectToArray: '$$elem.syllableCountMap', + }, }, - }, + ], }, }, }, - { - $match: randomContentQuery, - }, - { $sample: { size: limit - contentData.length } }, - ]) - .exec() - .then((doc) => { - for (const docEle of doc) { - if (contentData.length == 0 || !contentDataSet.has(docEle.contentId)) { - contentDataSet.add(docEle.contentId); - contentData.push(docEle); - } - } - }); + }, + }, + { + $match: randomContentQuery, + }, + { $sample: { size: limit - contentData.length } }, + ]) + .exec() + .then((doc) => { + for (const docEle of doc) { + if ( + contentData.length == 0 || + !contentDataSet.has(docEle.contentId) + ) { + contentDataSet.add(docEle.contentId); + contentData.push(docEle); + } + } + }); } if (contentData.length <= limit) { if (level_competency?.length > 0) { - query["level_complexity.level_competency"] = { $exists: true } + query['level_complexity.level_competency'] = { $exists: true }; } await this.content @@ -1353,7 +1404,10 @@ export class contentService { .exec() .then((doc) => { for (const docEle of doc) { - if (contentData.length == 0 || !contentDataSet.has(docEle.contentId)) { + if ( + contentData.length == 0 || + !contentDataSet.has(docEle.contentId) + ) { contentDataSet.add(docEle.contentId); contentData.push(docEle); } @@ -1363,52 +1417,55 @@ export class contentService { // remove all criteria and get random content with content type if (contentData.length <= limit) { - let randomContentQuery = { - contentSourceData: { - $elemMatch: { + let randomContentQuery = { + contentSourceData: { + $elemMatch: {}, }, - }, - contentType: contentType - }; + contentType: contentType, + }; - randomContentQuery.contentSourceData.$elemMatch['language'] = en_config.language_code; + randomContentQuery.contentSourceData.$elemMatch['language'] = + en_config.language_code; - await this.content - .aggregate([ - { - $addFields: { - contentSourceData: { - $map: { - input: '$contentSourceData', - as: 'elem', - in: { - $mergeObjects: [ - '$$elem', - { - syllableCountArray: { - $objectToArray: '$$elem.syllableCountMap', - }, + await this.content + .aggregate([ + { + $addFields: { + contentSourceData: { + $map: { + input: '$contentSourceData', + as: 'elem', + in: { + $mergeObjects: [ + '$$elem', + { + syllableCountArray: { + $objectToArray: '$$elem.syllableCountMap', + }, + }, + ], }, - ], + }, }, }, }, - }, - }, - { - $match: randomContentQuery, - }, - { $sample: { size: limit - contentData.length } }, - ]) - .exec() - .then((doc) => { - for (const docEle of doc) { - if (contentData.length == 0 || !contentDataSet.has(docEle.contentId)) { - contentDataSet.add(docEle.contentId); - contentData.push(docEle); - } - } - }); + { + $match: randomContentQuery, + }, + { $sample: { size: limit - contentData.length } }, + ]) + .exec() + .then((doc) => { + for (const docEle of doc) { + if ( + contentData.length == 0 || + !contentDataSet.has(docEle.contentId) + ) { + contentDataSet.add(docEle.contentId); + contentData.push(docEle); + } + } + }); } // Remove content level @@ -1480,25 +1537,39 @@ export class contentService { } } - async searchByFilter(syllableList, syllableCount, wordCount, totalOrthoComplexity, totalPhonicComplexity, meanComplexity, language, contentType, limit, contentId, collectionId, tags): Promise { + async searchByFilter( + syllableList, + syllableCount, + wordCount, + totalOrthoComplexity, + totalPhonicComplexity, + meanComplexity, + language, + contentType, + limit, + contentId, + collectionId, + tags, + ): Promise { if (syllableList == undefined || syllableList.length == 0) { - syllableList = [] + syllableList = []; } if (tags == undefined || tags.length == 0) { - tags = [] + tags = []; } if (language !== 'en') { - let mileStoneQuery = []; let cLevelQuery: any = []; - let contentQueryParam = []; let complexityQueryParam = []; - if (syllableCount !== undefined && Object.keys(syllableCount).length != 0) { + if ( + syllableCount !== undefined && + Object.keys(syllableCount).length != 0 + ) { contentQueryParam.push({ syllableCount: syllableCount }); } @@ -1506,15 +1577,28 @@ export class contentService { contentQueryParam.push({ wordCount: wordCount }); } - if (totalOrthoComplexity !== undefined && Object.keys(totalOrthoComplexity).length != 0) { - complexityQueryParam.push({ totalOrthoComplexity: totalOrthoComplexity }); + if ( + totalOrthoComplexity !== undefined && + Object.keys(totalOrthoComplexity).length != 0 + ) { + complexityQueryParam.push({ + totalOrthoComplexity: totalOrthoComplexity, + }); } - if (totalPhonicComplexity !== undefined && Object.keys(totalPhonicComplexity).length != 0) { - complexityQueryParam.push({ totalPhonicComplexity: totalPhonicComplexity }); + if ( + totalPhonicComplexity !== undefined && + Object.keys(totalPhonicComplexity).length != 0 + ) { + complexityQueryParam.push({ + totalPhonicComplexity: totalPhonicComplexity, + }); } - if (meanComplexity !== undefined && Object.keys(meanComplexity).length != 0) { + if ( + meanComplexity !== undefined && + Object.keys(meanComplexity).length != 0 + ) { complexityQueryParam.push({ meanPhonicComplexity: meanComplexity }); } @@ -1524,13 +1608,13 @@ export class contentService { mileStoneQuery.push(complexityQueryParamEle); } - let searchChar = syllableList.join("|"); + let searchChar = syllableList.join('|'); let unicodeArray = []; for (let syllableListEle of syllableList) { let unicodeCombination = ''; for (const [index, syllable] of syllableListEle.split('').entries()) { - let unicodeValue = "\\" + "u0" + syllable.charCodeAt(0).toString(16); + let unicodeValue = '\\' + 'u0' + syllable.charCodeAt(0).toString(16); unicodeCombination += index !== 0 ? '+' : ''; unicodeCombination += unicodeValue; } @@ -1543,34 +1627,38 @@ export class contentService { let query: any = {}; query = { - "contentType": contentType, - "contentSourceData": { - "$elemMatch": { - "language": language - } - } - } + contentType: contentType, + contentSourceData: { + $elemMatch: { + language: language, + }, + }, + }; if (mileStoneQuery !== undefined && mileStoneQuery.length > 0) { for (let mileStoneQueryEle of mileStoneQuery) { let ObjectKey = Object.keys(mileStoneQueryEle)[0]; - query.contentSourceData["$elemMatch"][ObjectKey] = Object.values(mileStoneQueryEle)[0] + query.contentSourceData['$elemMatch'][ObjectKey] = + Object.values(mileStoneQueryEle)[0]; } } if (cLevelQuery !== undefined && cLevelQuery.length > 0) { for (let cLevelQueryEle of cLevelQuery) { let ObjectKey = Object.keys(cLevelQueryEle)[0]; - query.contentSourceData["$elemMatch"][ObjectKey] = Object.values(cLevelQueryEle)[0] + query.contentSourceData['$elemMatch'][ObjectKey] = + Object.values(cLevelQueryEle)[0]; } } if (syllableList != undefined && syllableList.length > 0) { - query.contentSourceData["$elemMatch"]["text"] = { $regex: syllableRegexPattern } + query.contentSourceData['$elemMatch']['text'] = { + $regex: syllableRegexPattern, + }; } if (contentType === 'char') { - query.contentType = "Word" + query.contentType = 'Word'; } if (contentId !== undefined) { @@ -1585,40 +1673,48 @@ export class contentService { query.tags = { $all: tags }; } - await this.content.aggregate([ - { - $addFields: { - "contentSourceData": { - $map: { - input: "$contentSourceData", - as: "elem", - in: { - $mergeObjects: [ - "$$elem", - { - "syllableCountArray": { $objectToArray: "$$elem.syllableCountMap" } - } - ] - } - } - } + await this.content + .aggregate([ + { + $addFields: { + contentSourceData: { + $map: { + input: '$contentSourceData', + as: 'elem', + in: { + $mergeObjects: [ + '$$elem', + { + syllableCountArray: { + $objectToArray: '$$elem.syllableCountMap', + }, + }, + ], + }, + }, + }, + }, + }, + { + $match: query, + }, + { $sample: { size: limit } }, + ]) + .exec() + .then((doc) => { + for (let docEle of doc) { + let text: string = docEle.contentSourceData[0]['text'].trim(); + let matchedChar = text.match( + new RegExp(`(${unicodeArray.join('|')})`, 'gu'), + ); + wordsArr.push({ + ...docEle, + matchedChar: Array.from(new Set(matchedChar)), + }); } - }, - { - $match: query - }, - { $sample: { size: limit } } - ]).exec().then((doc) => { - for (let docEle of doc) { - let text: string = docEle.contentSourceData[0]['text'].trim(); - let matchedChar = text.match(new RegExp(`(${unicodeArray.join('|')})`, 'gu')); - wordsArr.push({ ...docEle, matchedChar: Array.from(new Set(matchedChar)) }); - } - }) - + }); if (wordsArr.length > 0) { - let textSet = new Set(); for (let wordsArrEle of wordsArr) { @@ -1630,14 +1726,14 @@ export class contentService { } if (textSet.size !== limit) { - for (let textSetEle of textSet) { let repeatCounter = 0; let deleteFlag = false; for (let [wordArrEleIndex, wordsArrEle] of wordsArr.entries()) { - if (wordsArrEle !== undefined) { - for (let contentSourceDataEle of wordsArrEle["contentSourceData"]) { + for (let contentSourceDataEle of wordsArrEle[ + 'contentSourceData' + ]) { if (contentSourceDataEle.language === language) { if (contentSourceDataEle.text.trim() === textSetEle) { if (repeatCounter === 1) { @@ -1651,33 +1747,33 @@ export class contentService { } if (deleteFlag === true) { - delete wordsArr[wordArrEleIndex]; } } - } } } - wordsArr = wordsArr.filter(element => { + wordsArr = wordsArr.filter((element) => { return element !== undefined; }); - } return { wordsArr: wordsArr }; - } else if (language === "en") { + } else if (language === 'en') { let wordsArr = []; let cLevelQuery: any; if (contentType.toLocaleLowerCase() === 'char') { - contentType = 'Word' + contentType = 'Word'; } let contentQueryParam = []; - if (syllableCount !== undefined && Object.keys(syllableCount).length != 0) { + if ( + syllableCount !== undefined && + Object.keys(syllableCount).length != 0 + ) { contentQueryParam.push({ syllableCount: syllableCount }); } @@ -1690,24 +1786,26 @@ export class contentService { let query: any = {}; query = { - "contentSourceData": { - "$elemMatch": { - "language": language, - } + contentSourceData: { + $elemMatch: { + language: language, + }, }, - "contentType": contentType, - } + contentType: contentType, + }; if (cLevelQuery !== undefined && cLevelQuery.length > 0) { for (let cLevelQueryEle of cLevelQuery) { let ObjectKey = Object.keys(cLevelQueryEle)[0]; - query.contentSourceData["$elemMatch"][ObjectKey] = Object.values(cLevelQueryEle)[0] + query.contentSourceData['$elemMatch'][ObjectKey] = + Object.values(cLevelQueryEle)[0]; } } - if (syllableList !== undefined && syllableList.length > 0) { - query.contentSourceData["$elemMatch"]["phonemes"] = { "$in": syllableList } + query.contentSourceData['$elemMatch']['phonemes'] = { + $in: syllableList, + }; } if (contentId !== undefined) { @@ -1722,39 +1820,48 @@ export class contentService { query.tags = { $all: tags }; } - await this.content.aggregate([ - { - $addFields: { - "contentSourceData": { - $map: { - input: "$contentSourceData", - as: "elem", - in: { - $mergeObjects: [ - "$$elem", - { - "syllableCountArray": { $objectToArray: "$$elem.syllableCountMap" } - } - ] - } - } - } + await this.content + .aggregate([ + { + $addFields: { + contentSourceData: { + $map: { + input: '$contentSourceData', + as: 'elem', + in: { + $mergeObjects: [ + '$$elem', + { + syllableCountArray: { + $objectToArray: '$$elem.syllableCountMap', + }, + }, + ], + }, + }, + }, + }, + }, + { + $match: query, + }, + { $sample: { size: limit } }, + ]) + .exec() + .then((doc) => { + for (let docEle of doc) { + let matchedTokens = syllableList.filter((token) => + docEle.contentSourceData[0].phonemes.includes(token), + ); + wordsArr.push({ + ...docEle, + matchedChar: Array.from(new Set(matchedTokens)), + }); } - }, - { - $match: query - }, - { $sample: { size: limit } } - ]).exec().then((doc) => { - for (let docEle of doc) { - let matchedTokens = syllableList.filter(token => docEle.contentSourceData[0].phonemes.includes(token)); - wordsArr.push({ ...docEle, matchedChar: Array.from(new Set(matchedTokens)) }); - } - }) + }); return { wordsArr: wordsArr }; } - } async charNotPresent(tokenArr): Promise { @@ -1839,18 +1946,18 @@ export class contentService { limit = 5, language, levelCompetencyArr, - tags + tags, ) { let queries = []; const numLevels = levelCompetencyArr.length; - + // Calculate split limit (e.g., for limit 5, splitLimit would be 3 for L1.1 and 2 for L1.2) const splitLimit = Math.ceil(limit / numLevels); - + if (numLevels > 0 && levelCompetencyArr !== undefined) { levelCompetencyArr.forEach((levelCompetency, levelCompetencyIndex) => { if (levelCompetencyIndex === 0) { - if(tags.length > 0){ + if (tags.length > 0) { queries.push( this.content.aggregate([ { @@ -1858,16 +1965,19 @@ export class contentService { contentType: contentType, language: language, mechanics_data: { - $elemMatch: { mechanics_id: mechanics_id, language: language } + $elemMatch: { + mechanics_id: mechanics_id, + language: language, + }, }, - "level_complexity.level_competency": levelCompetency, - "tags":{ $all: tags } - } + 'level_complexity.level_competency': levelCompetency, + tags: { $all: tags }, + }, }, - { $sample: { size: splitLimit } } // Fetch for the first level - ]) + { $sample: { size: splitLimit } }, // Fetch for the first level + ]), ); - }else{ + } else { queries.push( this.content.aggregate([ { @@ -1875,36 +1985,42 @@ export class contentService { contentType: contentType, language: language, mechanics_data: { - $elemMatch: { mechanics_id: mechanics_id, language: language } + $elemMatch: { + mechanics_id: mechanics_id, + language: language, + }, }, - "level_complexity.level_competency": levelCompetency - } + 'level_complexity.level_competency': levelCompetency, + }, }, - { $sample: { size: splitLimit } } // Fetch for the first level - ]) + { $sample: { size: splitLimit } }, // Fetch for the first level + ]), ); } } else { let handleLimit = limit % 2; - if(tags.length > 0){ - queries.push( - this.content.aggregate([ - { - $match: { - contentType: contentType, - language: language, - mechanics_data: { - $elemMatch: { mechanics_id: mechanics_id, language: language } + if (tags.length > 0) { + queries.push( + this.content.aggregate([ + { + $match: { + contentType: contentType, + language: language, + mechanics_data: { + $elemMatch: { + mechanics_id: mechanics_id, + language: language, + }, + }, + 'level_complexity.level_competency': levelCompetency, + tags: { $all: tags }, }, - "level_complexity.level_competency": levelCompetency, - "tags":{ $all: tags } - } - }, - { $sample: { size: splitLimit - handleLimit } } // Fetch fewer items for other levels - ]) - ); - }else{ + }, + { $sample: { size: splitLimit - handleLimit } }, // Fetch fewer items for other levels + ]), + ); + } else { queries.push( this.content.aggregate([ { @@ -1912,72 +2028,75 @@ export class contentService { contentType: contentType, language: language, mechanics_data: { - $elemMatch: { mechanics_id: mechanics_id, language: language } + $elemMatch: { + mechanics_id: mechanics_id, + language: language, + }, }, - "level_complexity.level_competency": levelCompetency - } + 'level_complexity.level_competency': levelCompetency, + }, }, - { $sample: { size: splitLimit - handleLimit } } // Fetch fewer items for other levels - ]) + { $sample: { size: splitLimit - handleLimit } }, // Fetch fewer items for other levels + ]), ); } } }); } - + let results = []; - + // Execute all queries and combine the results const queryResults = await Promise.all(queries); - + // Merge the results from different level competencies queryResults.forEach((queryResult) => { results = [...results, ...queryResult]; }); - + // Ensure total results don't exceed the limit if (results.length > limit) { results = results.slice(0, limit); } - + // If results are less than the limit, fetch additional content from any level if (results.length < limit) { const remainingLimit = limit - results.length; let additionalContent; - if(tags.length > 0){ - additionalContent= await this.content.aggregate([ - { - $match: { - contentType: contentType, - language: language, - mechanics_data: { - $elemMatch: { mechanics_id: mechanics_id, language: language } + if (tags.length > 0) { + additionalContent = await this.content.aggregate([ + { + $match: { + contentType: contentType, + language: language, + mechanics_data: { + $elemMatch: { mechanics_id: mechanics_id, language: language }, + }, + 'level_complexity.level_competency': { $exists: true }, + tags: { $all: tags }, }, - "level_complexity.level_competency": { $exists: true }, - "tags":{ $all: tags } - } - }, - { $sample: { size: remainingLimit } } - ]); - }else{ + }, + { $sample: { size: remainingLimit } }, + ]); + } else { additionalContent = await this.content.aggregate([ { $match: { contentType: contentType, language: language, mechanics_data: { - $elemMatch: { mechanics_id: mechanics_id, language: language } + $elemMatch: { mechanics_id: mechanics_id, language: language }, }, - "level_complexity.level_competency": { $exists: true } - } + 'level_complexity.level_competency': { $exists: true }, + }, }, - { $sample: { size: remainingLimit } } + { $sample: { size: remainingLimit } }, ]); } - + results = [...results, ...additionalContent]; } - + // If results are still less than the limit, fetch content without level_competency if (results.length < limit) { const remainingLimit = limit - results.length; @@ -1987,13 +2106,13 @@ export class contentService { contentType: contentType, language: language, mechanics_data: { - $elemMatch: { mechanics_id: mechanics_id, language: language } - } - } + $elemMatch: { mechanics_id: mechanics_id, language: language }, + }, + }, }, - { $sample: { size: remainingLimit } } + { $sample: { size: remainingLimit } }, ]); - + results = [...results, ...fallbackContent]; } @@ -2005,28 +2124,27 @@ export class contentService { $match: { contentType: contentType, language: language, - } + }, }, - { $sample: { size: remainingLimit } } + { $sample: { size: remainingLimit } }, ]); - + results = [...results, ...fallbackContent]; } - - let wordsArr = results.slice(0, limit); + let wordsArr = results.slice(0, limit); - wordsArr.map((content) => { - const { mechanics_data } = content; - if(mechanics_data){ - const mechanicData = mechanics_data.find( - (mechanic) => {return mechanic.mechanics_id === mechanics_id} - ); + wordsArr.map((content) => { + const { mechanics_data } = content; + if (mechanics_data) { + const mechanicData = mechanics_data.find((mechanic) => { + return mechanic.mechanics_id === mechanics_id; + }); content.mechanics_data = []; content.mechanics_data.push(mechanicData); } - return content; - }); + return content; + }); return { wordsArr: wordsArr }; } From dacf8690db311e779e906f27a5c0f7597cd8a4b4 Mon Sep 17 00:00:00 2001 From: DevendraPPatil Date: Wed, 11 Jun 2025 15:23:38 +0530 Subject: [PATCH 03/22] Feat: 1.Session expired feature added --- package-lock.json | 135 ++++++++++++++++++++++++++++++++++++++ package.json | 1 + src/app.module.ts | 2 + src/auth/auth.guard.ts | 13 +++- src/redis/redis.module.ts | 23 +++++++ 5 files changed, 173 insertions(+), 1 deletion(-) create mode 100644 src/redis/redis.module.ts diff --git a/package-lock.json b/package-lock.json index cac1599..3920b99 100644 --- a/package-lock.json +++ b/package-lock.json @@ -33,6 +33,7 @@ "jsonwebtoken": "^9.0.2", "mongoose": "^7.3.2", "passport-jwt": "^4.0.1", + "redis": "^5.5.6", "reflect-metadata": "^0.1.13", "rxjs": "^7.8.1", "split-graphemes": "^0.5.0", @@ -2185,6 +2186,66 @@ "node": ">=14" } }, + "node_modules/@redis/bloom": { + "version": "5.5.6", + "resolved": "https://registry.npmjs.org/@redis/bloom/-/bloom-5.5.6.tgz", + "integrity": "sha512-bNR3mxkwtfuCxNOzfV8B3R5zA1LiN57EH6zK4jVBIgzMzliNuReZXBFGnXvsi80/SYohajn78YdpYI+XNpqL+A==", + "license": "MIT", + "engines": { + "node": ">= 18" + }, + "peerDependencies": { + "@redis/client": "^5.5.6" + } + }, + "node_modules/@redis/client": { + "version": "5.5.6", + "resolved": "https://registry.npmjs.org/@redis/client/-/client-5.5.6.tgz", + "integrity": "sha512-M3Svdwt6oSfyfQdqEr0L2HOJH2vK7GgCFx1NfAQvpWAT4+ljoT1L5S5cKT3dA9NJrxrOPDkdoTPWJnIrGCOcmw==", + "license": "MIT", + "dependencies": { + "cluster-key-slot": "1.1.2" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/@redis/json": { + "version": "5.5.6", + "resolved": "https://registry.npmjs.org/@redis/json/-/json-5.5.6.tgz", + "integrity": "sha512-AIsoe3SsGQagqAmSQHaqxEinm5oCWr7zxPWL90kKaEdLJ+zw8KBznf2i9oK0WUFP5pFssSQUXqnscQKe2amfDQ==", + "license": "MIT", + "engines": { + "node": ">= 18" + }, + "peerDependencies": { + "@redis/client": "^5.5.6" + } + }, + "node_modules/@redis/search": { + "version": "5.5.6", + "resolved": "https://registry.npmjs.org/@redis/search/-/search-5.5.6.tgz", + "integrity": "sha512-JSqasYqO0mVcHL7oxvbySRBBZYRYhFl3W7f0Da7BW8M/r0Z9wCiVrdjnN4/mKBpWZkoJT/iuisLUdPGhpKxBew==", + "license": "MIT", + "engines": { + "node": ">= 18" + }, + "peerDependencies": { + "@redis/client": "^5.5.6" + } + }, + "node_modules/@redis/time-series": { + "version": "5.5.6", + "resolved": "https://registry.npmjs.org/@redis/time-series/-/time-series-5.5.6.tgz", + "integrity": "sha512-jkpcgq3NOI3TX7xEAJ3JgesJTxAx7k0m6lNxNsYdEM8KOl+xj7GaB/0CbLkoricZDmFSEAz7ClA1iK9XkGHf+Q==", + "license": "MIT", + "engines": { + "node": ">= 18" + }, + "peerDependencies": { + "@redis/client": "^5.5.6" + } + }, "node_modules/@sinclair/typebox": { "version": "0.27.8", "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.27.8.tgz", @@ -3754,6 +3815,15 @@ "node": ">=0.8" } }, + "node_modules/cluster-key-slot": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/cluster-key-slot/-/cluster-key-slot-1.1.2.tgz", + "integrity": "sha512-RMr0FhtfXemyinomL4hrWcYJxmX6deFdCxpJzhDttxgO1+bcCnkk+9drydLVDmAMG7NE6aN/fl4F7ucU/90gAA==", + "license": "Apache-2.0", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/co": { "version": "4.6.0", "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", @@ -8019,6 +8089,22 @@ "node": ">= 0.10" } }, + "node_modules/redis": { + "version": "5.5.6", + "resolved": "https://registry.npmjs.org/redis/-/redis-5.5.6.tgz", + "integrity": "sha512-hbpqBfcuhWHOS9YLNcXcJ4akNr7HFX61Dq3JuFZ9S7uU7C7kvnzuH2PDIXOP62A3eevvACoG8UacuXP3N07xdg==", + "license": "MIT", + "dependencies": { + "@redis/bloom": "5.5.6", + "@redis/client": "5.5.6", + "@redis/json": "5.5.6", + "@redis/search": "5.5.6", + "@redis/time-series": "5.5.6" + }, + "engines": { + "node": ">= 18" + } + }, "node_modules/reflect-metadata": { "version": "0.1.14", "resolved": "https://registry.npmjs.org/reflect-metadata/-/reflect-metadata-0.1.14.tgz", @@ -11277,6 +11363,38 @@ "dev": true, "optional": true }, + "@redis/bloom": { + "version": "5.5.6", + "resolved": "https://registry.npmjs.org/@redis/bloom/-/bloom-5.5.6.tgz", + "integrity": "sha512-bNR3mxkwtfuCxNOzfV8B3R5zA1LiN57EH6zK4jVBIgzMzliNuReZXBFGnXvsi80/SYohajn78YdpYI+XNpqL+A==", + "requires": {} + }, + "@redis/client": { + "version": "5.5.6", + "resolved": "https://registry.npmjs.org/@redis/client/-/client-5.5.6.tgz", + "integrity": "sha512-M3Svdwt6oSfyfQdqEr0L2HOJH2vK7GgCFx1NfAQvpWAT4+ljoT1L5S5cKT3dA9NJrxrOPDkdoTPWJnIrGCOcmw==", + "requires": { + "cluster-key-slot": "1.1.2" + } + }, + "@redis/json": { + "version": "5.5.6", + "resolved": "https://registry.npmjs.org/@redis/json/-/json-5.5.6.tgz", + "integrity": "sha512-AIsoe3SsGQagqAmSQHaqxEinm5oCWr7zxPWL90kKaEdLJ+zw8KBznf2i9oK0WUFP5pFssSQUXqnscQKe2amfDQ==", + "requires": {} + }, + "@redis/search": { + "version": "5.5.6", + "resolved": "https://registry.npmjs.org/@redis/search/-/search-5.5.6.tgz", + "integrity": "sha512-JSqasYqO0mVcHL7oxvbySRBBZYRYhFl3W7f0Da7BW8M/r0Z9wCiVrdjnN4/mKBpWZkoJT/iuisLUdPGhpKxBew==", + "requires": {} + }, + "@redis/time-series": { + "version": "5.5.6", + "resolved": "https://registry.npmjs.org/@redis/time-series/-/time-series-5.5.6.tgz", + "integrity": "sha512-jkpcgq3NOI3TX7xEAJ3JgesJTxAx7k0m6lNxNsYdEM8KOl+xj7GaB/0CbLkoricZDmFSEAz7ClA1iK9XkGHf+Q==", + "requires": {} + }, "@sinclair/typebox": { "version": "0.27.8", "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.27.8.tgz", @@ -12512,6 +12630,11 @@ "integrity": "sha512-JQHZ2QMW6l3aH/j6xCqQThY/9OH4D/9ls34cgkUBiEeocRTU04tHfKPBsUK1PqZCUQM7GiA0IIXJSuXHI64Kbg==", "dev": true }, + "cluster-key-slot": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/cluster-key-slot/-/cluster-key-slot-1.1.2.tgz", + "integrity": "sha512-RMr0FhtfXemyinomL4hrWcYJxmX6deFdCxpJzhDttxgO1+bcCnkk+9drydLVDmAMG7NE6aN/fl4F7ucU/90gAA==" + }, "co": { "version": "4.6.0", "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", @@ -15700,6 +15823,18 @@ "resolve": "^1.1.6" } }, + "redis": { + "version": "5.5.6", + "resolved": "https://registry.npmjs.org/redis/-/redis-5.5.6.tgz", + "integrity": "sha512-hbpqBfcuhWHOS9YLNcXcJ4akNr7HFX61Dq3JuFZ9S7uU7C7kvnzuH2PDIXOP62A3eevvACoG8UacuXP3N07xdg==", + "requires": { + "@redis/bloom": "5.5.6", + "@redis/client": "5.5.6", + "@redis/json": "5.5.6", + "@redis/search": "5.5.6", + "@redis/time-series": "5.5.6" + } + }, "reflect-metadata": { "version": "0.1.14", "resolved": "https://registry.npmjs.org/reflect-metadata/-/reflect-metadata-0.1.14.tgz", diff --git a/package.json b/package.json index d6e736e..de6d317 100644 --- a/package.json +++ b/package.json @@ -44,6 +44,7 @@ "jsonwebtoken": "^9.0.2", "mongoose": "^7.3.2", "passport-jwt": "^4.0.1", + "redis": "^5.5.6", "reflect-metadata": "^0.1.13", "rxjs": "^7.8.1", "split-graphemes": "^0.5.0", diff --git a/src/app.module.ts b/src/app.module.ts index ba69fc9..a658caf 100644 --- a/src/app.module.ts +++ b/src/app.module.ts @@ -11,6 +11,7 @@ import { CollectionController } from './controllers/collection.controller'; import { CollectionService } from './services/collection.service'; import { HttpModule } from '@nestjs/axios'; import { AuthModule } from './auth/auth.module'; +import { RedisModule } from './redis/redis.module'; @Module({ imports: [ @@ -38,6 +39,7 @@ import { AuthModule } from './auth/auth.module'; { name: collection.name, schema: collectionDbSchema }, ]), AuthModule, + RedisModule, ], controllers: [AppController, contentController, CollectionController], providers: [AppService, contentService, CollectionService], diff --git a/src/auth/auth.guard.ts b/src/auth/auth.guard.ts index 4c7aa6c..3d0414e 100644 --- a/src/auth/auth.guard.ts +++ b/src/auth/auth.guard.ts @@ -3,15 +3,19 @@ import { CanActivate, ExecutionContext, UnauthorizedException, + Inject, } from '@nestjs/common'; import { JwtService } from '@nestjs/jwt'; import { createHash } from 'crypto'; import { Request } from 'express'; import * as jose from 'jose'; +import { RedisClientType } from 'redis'; @Injectable() export class JwtAuthGuard implements CanActivate { - constructor(private jwtService: JwtService) {} + constructor(private jwtService: JwtService, + @Inject('REDIS_CLIENT') private readonly redisClient: RedisClientType, + ) {} async canActivate(context: ExecutionContext): Promise { const request = context.switchToHttp().getRequest(); @@ -21,10 +25,17 @@ export class JwtAuthGuard implements CanActivate { } const token = authHeader.split(' ')[1]; try { + // Check Redis Blacklist + const isBlacklisted = await this.redisClient.get(`blacklist:${token}`); + if (isBlacklisted) { + throw new UnauthorizedException('Token has been logged out'); + } + //Step 1: Correctly Generate Encryption Key const secret_key = process.env.JOSE_SECRET || ''; const hash = createHash('sha256').update(secret_key).digest(); + //Step 2: Decrypt the Token const jwtDecryptedToken = await jose.jwtDecrypt(token, hash); diff --git a/src/redis/redis.module.ts b/src/redis/redis.module.ts new file mode 100644 index 0000000..6bceac2 --- /dev/null +++ b/src/redis/redis.module.ts @@ -0,0 +1,23 @@ +import { Module, Global } from '@nestjs/common'; +import { createClient } from 'redis'; + +const redisClient = createClient({ + socket: { + host: process.env.REDIS_HOST || '127.0.0.1', + port: parseInt(process.env.REDIS_PORT || '6379'), + }, +}); + +redisClient.connect().catch(console.error); + +@Global() +@Module({ + providers: [ + { + provide: 'REDIS_CLIENT', + useValue: redisClient, + }, + ], + exports: ['REDIS_CLIENT'], +}) +export class RedisModule {} From c765d63d82b1ba5faef4ddc891e36ed428bfb059 Mon Sep 17 00:00:00 2001 From: DevendraPPatil Date: Thu, 12 Jun 2025 12:40:08 +0530 Subject: [PATCH 04/22] Feat: password added for the redis --- src/redis/redis.module.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/redis/redis.module.ts b/src/redis/redis.module.ts index 6bceac2..cfdb00b 100644 --- a/src/redis/redis.module.ts +++ b/src/redis/redis.module.ts @@ -6,6 +6,7 @@ const redisClient = createClient({ host: process.env.REDIS_HOST || '127.0.0.1', port: parseInt(process.env.REDIS_PORT || '6379'), }, + password: process.env.REDIS_PASSWORD, }); redisClient.connect().catch(console.error); From 797168b657220f6d397b3e11c1ec479f411b268c Mon Sep 17 00:00:00 2001 From: vishnu vinay Date: Fri, 13 Jun 2025 21:43:46 +0530 Subject: [PATCH 05/22] Task : Change the complexity values with new Complexity for hi --- src/config/newComplexityConfig.ts | 165 ++++++++++++++++++++++++++++++ src/services/content.service.ts | 30 ++++-- 2 files changed, 186 insertions(+), 9 deletions(-) create mode 100644 src/config/newComplexityConfig.ts diff --git a/src/config/newComplexityConfig.ts b/src/config/newComplexityConfig.ts new file mode 100644 index 0000000..16ce788 --- /dev/null +++ b/src/config/newComplexityConfig.ts @@ -0,0 +1,165 @@ +var common_configs = { + contentLevel: [ + { + level: 'L1', + syllableCount: { $eq: 2 }, + language: 'ta', + contentType: 'Word', + }, + { + level: 'L1', + wordCount: { $gte: 2, $lte: 3 }, + language: 'ta', + contentType: 'Sentence', + }, + { + level: 'L2', + syllableCount: { $gte: 2, $lte: 3 }, + language: 'ta', + contentType: 'Word', + }, + { + level: 'L2', + wordCount: { $gte: 2, $lte: 3 }, + syllableCount: { $lte: 8 }, + syllableCountArray: { + $not: { + $elemMatch: { + v: { $gte: 4 }, + }, + }, + }, + language: 'ta', + contentType: 'Sentence', + }, + { + level: 'L3', + syllableCount: { $gte: 4 }, + language: 'ta', + contentType: 'Word', + }, + { + level: 'L3', + wordCount: { $gt: 2, $lte: 5 }, + syllableCount: { $lte: 15 }, + language: 'ta', + syllableCountArray: { + $not: { + $elemMatch: { + v: { $gte: 5 }, + }, + }, + }, + contentType: 'Sentence', + }, + { + level: 'L3', + wordCount: { $lte: 10 }, + language: 'ta', + contentType: 'Paragraph', + }, + { + level: 'L4', + wordCount: { $gt: 5, $lte: 7 }, + syllableCount: { $lte: 20 }, + language: 'ta', + syllableCountArray: { + $not: { + $elemMatch: { + v: { $gte: 7 }, + }, + }, + }, + contentType: 'Sentence', + }, + { + level: 'L4', + wordCount: { $lte: 10 }, + language: 'ta', + contentType: 'Paragraph', + }, + { + level: 'L5', + wordCount: { $gte: 7, $lte: 10 }, + language: 'ta', + contentType: 'Sentence', + }, + { + level: 'L5', + wordCount: { $gt: 10, $lte: 15 }, + language: 'ta', + contentType: 'Paragraph', + }, + { + level: 'L6', + wordCount: { $gte: 7, $lte: 12 }, + language: 'ta', + contentType: 'Sentence', + }, + { + level: 'L6', + wordCount: { $gt: 15 }, + language: 'ta', + contentType: 'Paragraph', + }, + ], + complexity: [ + { + level: 'C0', + readingComplexity : {$lt:1}, + language: 'ta', + contentType: 'Word', + }, + { + level: 'C1', + readingComplexity : { $gte :1 ,$lte:8 }, + language: 'ta', + contentType: 'Word', + }, + { + level: 'C1', + readingComplexity : { $lte:12 }, + language: 'ta', + contentType: 'Sentence', + }, + { + level: 'C2', + readingComplexity : { $lt:15 }, + language: 'ta', + contentType: 'Word', + }, + { + level: 'C2', + language: 'ta', + readingComplexity : { $lte:30 }, + contentType: 'Sentence', + }, + { + level: 'C3', + readingComplexity : { $lte:20}, + language: 'ta', + contentType: 'Word', + }, + { + level: 'C3', + readingComplexity : { $lte:50}, + language: 'ta', + contentType: 'Sentence', + }, + { + level: 'C4', + readingComplexity : { $gt:20}, + language: 'ta', + contentType: 'Word', + }, + { + level: 'C4', + readingComplexity : { $gt:50}, + language: 'ta', + contentType: 'Sentence', + }, + ], + }; + + export default common_configs; + \ No newline at end of file diff --git a/src/services/content.service.ts b/src/services/content.service.ts index 716c6be..934c919 100644 --- a/src/services/content.service.ts +++ b/src/services/content.service.ts @@ -5,6 +5,7 @@ import { content, contentDocument } from '../schemas/content.schema'; import { HttpService } from '@nestjs/axios'; import en_config from 'src/config/language/en'; import common_config from 'src/config/commonConfig'; +import common_configs from 'src/config/newComplexityConfig'; @Injectable() export class contentService { @@ -334,6 +335,7 @@ export class contentService { level_competency = [], ): Promise { let nextTokenArr = []; + let newComplexityLang =['hi']; if (tokenArr.length >= limit * 2) { nextTokenArr = tokenArr.slice(limit, limit * 2); } else { @@ -347,8 +349,9 @@ export class contentService { let prevContentLevel = ''; let contentQueryParam = []; let complexityQueryParam = []; - let contentLevel = common_config.contentLevel; - let complexity = common_config.complexity; + const configs = newComplexityLang.includes(language) ? common_configs : common_config; + let contentLevel = configs.contentLevel; + let complexity = configs.complexity; if (cLevel != '' || complexityLevel.length != 0) { if (cLevel != 'L1') { @@ -388,13 +391,22 @@ export class contentService { delete complexityQueryParamEle.level; delete complexityQueryParamEle.contentType; delete complexityQueryParamEle.language; - mileStoneQuery.push({ - totalPhonicComplexity: - complexityQueryParamEle.totalPhonicComplexity, - }); - mileStoneQuery.push({ - totalOrthoComplexity: complexityQueryParamEle.totalOrthoComplexity, - }); + if(newComplexityLang.includes(language)) { + mileStoneQuery.push({ + readingComplexity: + complexityQueryParamEle.readingComplexity, + }); + + } + else { + mileStoneQuery.push({ + totalPhonicComplexity: + complexityQueryParamEle.totalPhonicComplexity, + }); + mileStoneQuery.push({ + totalOrthoComplexity: complexityQueryParamEle.totalOrthoComplexity, + }); + } } } From b2738ba0aed2f35eb3368355a1e067195428eadd Mon Sep 17 00:00:00 2001 From: Ishan-ttpl Date: Mon, 16 Jun 2025 11:49:38 +0530 Subject: [PATCH 06/22] Create deploy.yml --- .github/workflows/deploy.yml | 100 +++++++++++++++++++++++++++++++++++ 1 file changed, 100 insertions(+) create mode 100644 .github/workflows/deploy.yml diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml new file mode 100644 index 0000000..9246136 --- /dev/null +++ b/.github/workflows/deploy.yml @@ -0,0 +1,100 @@ +name: Deploy or Rollback All-Content-Service + +on: + push: + tags: + - 'deploy-tn-staging-*' + workflow_dispatch: + inputs: + action: + description: 'Action to perform' + required: true + default: 'deploy' + type: choice + options: + - deploy + - rollback + environment: + description: 'Target environment' + required: true + default: 'tn-staging' + type: choice + options: + - tn-staging + branch: + description: 'Branch to deploy (only for "deploy" action)' + required: false + rollback_tag: + description: 'Tag to rollback to (only for "rollback" action, e.g., deploy-tn-staging-v1.0.0)' + required: false + +jobs: + deploy-or-rollback: + runs-on: ubuntu-latest + environment: tn-staging + if: github.event_name == 'push' || github.event_name == 'workflow_dispatch' + + steps: + - name: Determine environment and action + id: vars + run: | + ENVIRONMENT="" + ACTION="" + if [[ "${{ github.event_name }}" == "push" && "${{ github.ref }}" == refs/tags/* ]]; then + TAG_PREFIX="${GITHUB_REF#refs/tags/deploy-}" + if [[ "$TAG_PREFIX" == "tn-staging"* ]]; then + ENVIRONMENT="tn-staging" + ACTION="deploy" + echo "Determined action: deploy via tag push to tn-staging environment." + else + echo "::error::Invalid deploy tag format. Expected 'deploy-tn-staging-*'." + exit 1 + fi + else + ENVIRONMENT="${{ github.event.inputs.environment }}" + ACTION="${{ github.event.inputs.action }}" + echo "Workflow dispatched: Action='$ACTION', Environment='$ENVIRONMENT'." + fi + echo "environment=$ENVIRONMENT" >> "$GITHUB_OUTPUT" + echo "action=$ACTION" >> "$GITHUB_OUTPUT" + + - name: Checkout repository + uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: Set SSH Key and Target Host + run: | + echo "${{ secrets.SSH_KEY }}" > key.pem + echo "${{ secrets.SSH_KEY }}" > jump_key.pem + chmod 600 key.pem jump_key.pem + echo "host=${{ secrets.TN_STAGING_HOST }}" >> "$GITHUB_ENV" + echo "target_dir=/home/all-staging-user/all-services/all-content-deploy" >> "$GITHUB_ENV" + echo "jump_host=${{ secrets.JUMPHOST }}" >> "$GITHUB_ENV" + + - name: Perform Deploy or Rollback + run: | + ACTION="${{ steps.vars.outputs.action }}" + HOST="${{ env.host }}" + TARGET_DIR="${{ env.target_dir }}" + JUMP_HOST="${{ env.jump_host }}" + SSH_COMMAND="ssh -o StrictHostKeyChecking=no -i jump_key.pem -J $JUMP_HOST -i key.pem $HOST" + SCP_COMMAND="scp -o StrictHostKeyChecking=no -i jump_key.pem -o ProxyJump=$JUMP_HOST -i key.pem" + if [[ "$ACTION" == "deploy" ]]; then + BRANCH="${{ github.event.inputs.branch || github.ref_name }}" + echo "Initiating deployment to $HOST from branch: $BRANCH" + $SCP_COMMAND deploy.sh $HOST:/home/all-staging-user/deploy.sh + $SSH_COMMAND "chmod +x /home/all-staging-user/deploy.sh && /home/all-staging-user/deploy.sh $TARGET_DIR $BRANCH deploy" + elif [[ "$ACTION" == "rollback" ]]; then + ROLLBACK_TAG="${{ github.event.inputs.rollback_tag }}" + if [[ -z "$ROLLBACK_TAG" ]]; then + echo "::error::Rollback tag is required for rollback action." + exit 1 + fi + echo "Initiating rollback to tag: $ROLLBACK_TAG on $HOST" + $SCP_COMMAND deploy.sh $HOST:/home/all-staging-user/deploy.sh + $SSH_COMMAND "chmod +x /home/all-staging-user/deploy.sh && /home/all-staging-user/deploy.sh $TARGET_DIR $ROLLBACK_TAG rollback" + else + echo "::error::Invalid action specified: $ACTION" + exit 1 + fi From 09ec80d59ff53884fb11dd7aba239ef44975b8c5 Mon Sep 17 00:00:00 2001 From: Ishan-ttpl Date: Mon, 16 Jun 2025 12:00:35 +0530 Subject: [PATCH 07/22] Update Dev.yml --- .github/workflows/Dev.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/Dev.yml b/.github/workflows/Dev.yml index 04c1023..f663b41 100644 --- a/.github/workflows/Dev.yml +++ b/.github/workflows/Dev.yml @@ -2,7 +2,7 @@ name: DEV DEPLOYMENT on: push: - branches: [ lais-v2.0 ] + branches: [ -v2.0 ] jobs: build: runs-on: ubuntu-latest From 57e2e3d6945a73bd40dba2b57aa900f5cf591833 Mon Sep 17 00:00:00 2001 From: Ishan-ttpl Date: Mon, 16 Jun 2025 12:09:48 +0530 Subject: [PATCH 08/22] Create deploy.sh --- deploy.sh | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) create mode 100644 deploy.sh diff --git a/deploy.sh b/deploy.sh new file mode 100644 index 0000000..324f486 --- /dev/null +++ b/deploy.sh @@ -0,0 +1,28 @@ +#!/bin/bash +set -e +TARGET_DIR="$1" +REF="$2" # Branch for deploy, tag for rollback +ACTION="$3" # deploy or rollback + +cd "$TARGET_DIR" || { echo "Error: Directory $TARGET_DIR not found."; exit 1; } +echo "Fetching all latest changes..." +git fetch --all +if [ "$ACTION" = "deploy" ]; then + echo "Checking out branch: $REF..." + git checkout "$REF" || { echo "Error: Failed to checkout branch $REF."; exit 1; } + echo "Pulling latest changes for $REF..." + git pull origin "$REF" || { echo "Error: Failed to pull from origin $REF."; exit 1; } +elif [ "$ACTION" = "rollback" ]; then + echo "Checking out tag: $REF..." + git checkout "tags/$REF" || { echo "Error: Failed to checkout tag $REF."; exit 1; } +else + echo "Error: Invalid action $ACTION. Expected 'deploy' or 'rollback'." + exit 1 +fi +echo "Stopping existing Docker containers..." +docker-compose down || echo "No running containers to stop." +echo "Building and starting new Docker containers..." +docker-compose up -d --build || { echo "Error: Docker compose failed."; exit 1; } +echo "Cleaning dangling Docker images..." +docker images --no-trunc -aqf "dangling=true" | xargs docker rmi || echo "No dangling images." +echo "$ACTION complete for $REF." From baade57a0f7d0694879536a31f3f1e2cd804c69c Mon Sep 17 00:00:00 2001 From: Ishan-ttpl Date: Mon, 16 Jun 2025 14:41:03 +0530 Subject: [PATCH 09/22] Update deploy.yml --- .github/workflows/deploy.yml | 64 +++++++++++++++++------------------- 1 file changed, 31 insertions(+), 33 deletions(-) diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index 9246136..d06ee2a 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -1,9 +1,6 @@ name: Deploy or Rollback All-Content-Service on: - push: - tags: - - 'deploy-tn-staging-*' workflow_dispatch: inputs: action: @@ -23,7 +20,8 @@ on: - tn-staging branch: description: 'Branch to deploy (only for "deploy" action)' - required: false + required: true + default: 'lais-v2.0' rollback_tag: description: 'Tag to rollback to (only for "rollback" action, e.g., deploy-tn-staging-v1.0.0)' required: false @@ -32,36 +30,29 @@ jobs: deploy-or-rollback: runs-on: ubuntu-latest environment: tn-staging - if: github.event_name == 'push' || github.event_name == 'workflow_dispatch' steps: - - name: Determine environment and action - id: vars - run: | - ENVIRONMENT="" - ACTION="" - if [[ "${{ github.event_name }}" == "push" && "${{ github.ref }}" == refs/tags/* ]]; then - TAG_PREFIX="${GITHUB_REF#refs/tags/deploy-}" - if [[ "$TAG_PREFIX" == "tn-staging"* ]]; then - ENVIRONMENT="tn-staging" - ACTION="deploy" - echo "Determined action: deploy via tag push to tn-staging environment." - else - echo "::error::Invalid deploy tag format. Expected 'deploy-tn-staging-*'." - exit 1 - fi - else - ENVIRONMENT="${{ github.event.inputs.environment }}" - ACTION="${{ github.event.inputs.action }}" - echo "Workflow dispatched: Action='$ACTION', Environment='$ENVIRONMENT'." - fi - echo "environment=$ENVIRONMENT" >> "$GITHUB_OUTPUT" - echo "action=$ACTION" >> "$GITHUB_OUTPUT" - - - name: Checkout repository + - name: Checkout selected branch uses: actions/checkout@v4 with: fetch-depth: 0 + ref: ${{ github.event.inputs.branch }} + + - name: Verify and locate deploy.sh + id: locate_deploy + run: | + DEPLOY_PATH=$(find "$GITHUB_WORKSPACE" -name deploy.sh -type f -print -quit) + + if [ -z "$DEPLOY_PATH" ]; then + echo "::error::deploy.sh not found anywhere in workspace!" + echo "Searching in: $GITHUB_WORKSPACE" + echo "Directory contents:" + ls -laR "$GITHUB_WORKSPACE" + exit 1 + fi + + echo "Found deploy.sh at: $DEPLOY_PATH" + echo "DEPLOY_SH_PATH=$DEPLOY_PATH" >> $GITHUB_ENV - name: Set SSH Key and Target Host run: | @@ -74,17 +65,24 @@ jobs: - name: Perform Deploy or Rollback run: | - ACTION="${{ steps.vars.outputs.action }}" + ACTION="${{ github.event.inputs.action }}" HOST="${{ env.host }}" TARGET_DIR="${{ env.target_dir }}" JUMP_HOST="${{ env.jump_host }}" + DEPLOY_SH_PATH="${{ env.DEPLOY_SH_PATH }}" + + echo "Using deploy.sh at: $DEPLOY_SH_PATH" + [ -f "$DEPLOY_SH_PATH" ] || { echo "::error::deploy.sh not found at $DEPLOY_SH_PATH"; exit 1; } + SSH_COMMAND="ssh -o StrictHostKeyChecking=no -i jump_key.pem -J $JUMP_HOST -i key.pem $HOST" SCP_COMMAND="scp -o StrictHostKeyChecking=no -i jump_key.pem -o ProxyJump=$JUMP_HOST -i key.pem" + if [[ "$ACTION" == "deploy" ]]; then - BRANCH="${{ github.event.inputs.branch || github.ref_name }}" + BRANCH="${{ github.event.inputs.branch }}" echo "Initiating deployment to $HOST from branch: $BRANCH" - $SCP_COMMAND deploy.sh $HOST:/home/all-staging-user/deploy.sh + $SCP_COMMAND "$DEPLOY_SH_PATH" $HOST:/home/all-staging-user/deploy.sh $SSH_COMMAND "chmod +x /home/all-staging-user/deploy.sh && /home/all-staging-user/deploy.sh $TARGET_DIR $BRANCH deploy" + elif [[ "$ACTION" == "rollback" ]]; then ROLLBACK_TAG="${{ github.event.inputs.rollback_tag }}" if [[ -z "$ROLLBACK_TAG" ]]; then @@ -92,7 +90,7 @@ jobs: exit 1 fi echo "Initiating rollback to tag: $ROLLBACK_TAG on $HOST" - $SCP_COMMAND deploy.sh $HOST:/home/all-staging-user/deploy.sh + $SCP_COMMAND "$DEPLOY_SH_PATH" $HOST:/home/all-staging-user/deploy.sh $SSH_COMMAND "chmod +x /home/all-staging-user/deploy.sh && /home/all-staging-user/deploy.sh $TARGET_DIR $ROLLBACK_TAG rollback" else echo "::error::Invalid action specified: $ACTION" From a4b11d09dc4756ec08f4f2573ac25121590ea431 Mon Sep 17 00:00:00 2001 From: Ishan-ttpl Date: Mon, 16 Jun 2025 14:50:37 +0530 Subject: [PATCH 10/22] Update deploy.yml --- .github/workflows/deploy.yml | 16 ++++++---------- 1 file changed, 6 insertions(+), 10 deletions(-) diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index d06ee2a..7f02f38 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -20,7 +20,7 @@ on: - tn-staging branch: description: 'Branch to deploy (only for "deploy" action)' - required: true + required: false default: 'lais-v2.0' rollback_tag: description: 'Tag to rollback to (only for "rollback" action, e.g., deploy-tn-staging-v1.0.0)' @@ -32,25 +32,22 @@ jobs: environment: tn-staging steps: - - name: Checkout selected branch + - name: Checkout repository uses: actions/checkout@v4 with: fetch-depth: 0 - ref: ${{ github.event.inputs.branch }} + ref: ${{ github.event.inputs.branch || 'lais-v2.0' }} - - name: Verify and locate deploy.sh + - name: Locate deploy.sh id: locate_deploy run: | DEPLOY_PATH=$(find "$GITHUB_WORKSPACE" -name deploy.sh -type f -print -quit) - if [ -z "$DEPLOY_PATH" ]; then - echo "::error::deploy.sh not found anywhere in workspace!" - echo "Searching in: $GITHUB_WORKSPACE" - echo "Directory contents:" + echo "::error::deploy.sh not found!" + echo "Searched in: $GITHUB_WORKSPACE" ls -laR "$GITHUB_WORKSPACE" exit 1 fi - echo "Found deploy.sh at: $DEPLOY_PATH" echo "DEPLOY_SH_PATH=$DEPLOY_PATH" >> $GITHUB_ENV @@ -82,7 +79,6 @@ jobs: echo "Initiating deployment to $HOST from branch: $BRANCH" $SCP_COMMAND "$DEPLOY_SH_PATH" $HOST:/home/all-staging-user/deploy.sh $SSH_COMMAND "chmod +x /home/all-staging-user/deploy.sh && /home/all-staging-user/deploy.sh $TARGET_DIR $BRANCH deploy" - elif [[ "$ACTION" == "rollback" ]]; then ROLLBACK_TAG="${{ github.event.inputs.rollback_tag }}" if [[ -z "$ROLLBACK_TAG" ]]; then From 641236fdd7cb91256f6b202da6811523c6fe96de Mon Sep 17 00:00:00 2001 From: vishnu vinay Date: Mon, 16 Jun 2025 14:54:52 +0530 Subject: [PATCH 11/22] Task : Change the variable names --- ...{newComplexityConfig.ts => readingComplexityConfig.ts} | 0 src/services/content.service.ts | 8 ++++---- 2 files changed, 4 insertions(+), 4 deletions(-) rename src/config/{newComplexityConfig.ts => readingComplexityConfig.ts} (100%) diff --git a/src/config/newComplexityConfig.ts b/src/config/readingComplexityConfig.ts similarity index 100% rename from src/config/newComplexityConfig.ts rename to src/config/readingComplexityConfig.ts diff --git a/src/services/content.service.ts b/src/services/content.service.ts index 934c919..fda6037 100644 --- a/src/services/content.service.ts +++ b/src/services/content.service.ts @@ -5,7 +5,7 @@ import { content, contentDocument } from '../schemas/content.schema'; import { HttpService } from '@nestjs/axios'; import en_config from 'src/config/language/en'; import common_config from 'src/config/commonConfig'; -import common_configs from 'src/config/newComplexityConfig'; +import common_configs from 'src/config/readingComplexityConfig'; @Injectable() export class contentService { @@ -335,7 +335,7 @@ export class contentService { level_competency = [], ): Promise { let nextTokenArr = []; - let newComplexityLang =['hi']; + let readingComplexityLang =['hi']; if (tokenArr.length >= limit * 2) { nextTokenArr = tokenArr.slice(limit, limit * 2); } else { @@ -349,7 +349,7 @@ export class contentService { let prevContentLevel = ''; let contentQueryParam = []; let complexityQueryParam = []; - const configs = newComplexityLang.includes(language) ? common_configs : common_config; + const configs = readingComplexityLang.includes(language) ? common_configs : common_config; let contentLevel = configs.contentLevel; let complexity = configs.complexity; @@ -391,7 +391,7 @@ export class contentService { delete complexityQueryParamEle.level; delete complexityQueryParamEle.contentType; delete complexityQueryParamEle.language; - if(newComplexityLang.includes(language)) { + if(readingComplexityLang.includes(language)) { mileStoneQuery.push({ readingComplexity: complexityQueryParamEle.readingComplexity, From 61ea00b3e51b3cc04a6f29d9c25621d2e9e9f2d9 Mon Sep 17 00:00:00 2001 From: Ishan-ttpl Date: Mon, 16 Jun 2025 15:03:27 +0530 Subject: [PATCH 12/22] Update deploy.yml --- .github/workflows/deploy.yml | 38 ++++++++++++------------------------ 1 file changed, 13 insertions(+), 25 deletions(-) diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index 7f02f38..bf0bfec 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -1,6 +1,9 @@ name: Deploy or Rollback All-Content-Service on: + push: + tags: + - 'deploy-tn-staging-*' workflow_dispatch: inputs: action: @@ -30,6 +33,7 @@ jobs: deploy-or-rollback: runs-on: ubuntu-latest environment: tn-staging + if: github.event_name == 'push' || github.event_name == 'workflow_dispatch' steps: - name: Checkout repository @@ -38,19 +42,6 @@ jobs: fetch-depth: 0 ref: ${{ github.event.inputs.branch || 'lais-v2.0' }} - - name: Locate deploy.sh - id: locate_deploy - run: | - DEPLOY_PATH=$(find "$GITHUB_WORKSPACE" -name deploy.sh -type f -print -quit) - if [ -z "$DEPLOY_PATH" ]; then - echo "::error::deploy.sh not found!" - echo "Searched in: $GITHUB_WORKSPACE" - ls -laR "$GITHUB_WORKSPACE" - exit 1 - fi - echo "Found deploy.sh at: $DEPLOY_PATH" - echo "DEPLOY_SH_PATH=$DEPLOY_PATH" >> $GITHUB_ENV - - name: Set SSH Key and Target Host run: | echo "${{ secrets.SSH_KEY }}" > key.pem @@ -62,23 +53,19 @@ jobs: - name: Perform Deploy or Rollback run: | - ACTION="${{ github.event.inputs.action }}" + ACTION="${{ github.event.inputs.action || 'deploy' }}" HOST="${{ env.host }}" TARGET_DIR="${{ env.target_dir }}" JUMP_HOST="${{ env.jump_host }}" - DEPLOY_SH_PATH="${{ env.DEPLOY_SH_PATH }}" - - echo "Using deploy.sh at: $DEPLOY_SH_PATH" - [ -f "$DEPLOY_SH_PATH" ] || { echo "::error::deploy.sh not found at $DEPLOY_SH_PATH"; exit 1; } - + SSH_COMMAND="ssh -o StrictHostKeyChecking=no -i jump_key.pem -J $JUMP_HOST -i key.pem $HOST" - SCP_COMMAND="scp -o StrictHostKeyChecking=no -i jump_key.pem -o ProxyJump=$JUMP_HOST -i key.pem" if [[ "$ACTION" == "deploy" ]]; then - BRANCH="${{ github.event.inputs.branch }}" + BRANCH="${{ github.event.inputs.branch || 'lais-v2.0' }}" echo "Initiating deployment to $HOST from branch: $BRANCH" - $SCP_COMMAND "$DEPLOY_SH_PATH" $HOST:/home/all-staging-user/deploy.sh - $SSH_COMMAND "chmod +x /home/all-staging-user/deploy.sh && /home/all-staging-user/deploy.sh $TARGET_DIR $BRANCH deploy" + $SSH_COMMAND "cd $TARGET_DIR && \ + chmod +x deploy.sh && \ + ./deploy.sh $TARGET_DIR $BRANCH deploy" elif [[ "$ACTION" == "rollback" ]]; then ROLLBACK_TAG="${{ github.event.inputs.rollback_tag }}" if [[ -z "$ROLLBACK_TAG" ]]; then @@ -86,8 +73,9 @@ jobs: exit 1 fi echo "Initiating rollback to tag: $ROLLBACK_TAG on $HOST" - $SCP_COMMAND "$DEPLOY_SH_PATH" $HOST:/home/all-staging-user/deploy.sh - $SSH_COMMAND "chmod +x /home/all-staging-user/deploy.sh && /home/all-staging-user/deploy.sh $TARGET_DIR $ROLLBACK_TAG rollback" + $SSH_COMMAND "cd $TARGET_DIR && \ + chmod +x deploy.sh && \ + ./deploy.sh $TARGET_DIR $ROLLBACK_TAG rollback" else echo "::error::Invalid action specified: $ACTION" exit 1 From 1394f0d676c053c8eedbca6a8068b8509006f046 Mon Sep 17 00:00:00 2001 From: vishnu vinay Date: Mon, 16 Jun 2025 15:05:33 +0530 Subject: [PATCH 13/22] Fix : Profiling the code --- src/config/commonConfig.ts | 56 +++++++++ src/config/readingComplexityConfig.ts | 165 -------------------------- src/services/content.service.ts | 6 +- 3 files changed, 58 insertions(+), 169 deletions(-) delete mode 100644 src/config/readingComplexityConfig.ts diff --git a/src/config/commonConfig.ts b/src/config/commonConfig.ts index 85728aa..8f492a4 100644 --- a/src/config/commonConfig.ts +++ b/src/config/commonConfig.ts @@ -165,6 +165,62 @@ var common_config = { contentType: 'Sentence', }, ], + readingComplexity: [ + { + level: 'C0', + readingComplexity : {$lt:1}, + language: 'ta', + contentType: 'Word', + }, + { + level: 'C1', + readingComplexity : { $gte :1 ,$lte:8 }, + language: 'ta', + contentType: 'Word', + }, + { + level: 'C1', + readingComplexity : { $lte:12 }, + language: 'ta', + contentType: 'Sentence', + }, + { + level: 'C2', + readingComplexity : { $lt:15 }, + language: 'ta', + contentType: 'Word', + }, + { + level: 'C2', + language: 'ta', + readingComplexity : { $lte:30 }, + contentType: 'Sentence', + }, + { + level: 'C3', + readingComplexity : { $lte:20}, + language: 'ta', + contentType: 'Word', + }, + { + level: 'C3', + readingComplexity : { $lte:50}, + language: 'ta', + contentType: 'Sentence', + }, + { + level: 'C4', + readingComplexity : { $gt:20}, + language: 'ta', + contentType: 'Word', + }, + { + level: 'C4', + readingComplexity : { $gt:50}, + language: 'ta', + contentType: 'Sentence', + }, + ], }; export default common_config; diff --git a/src/config/readingComplexityConfig.ts b/src/config/readingComplexityConfig.ts deleted file mode 100644 index 16ce788..0000000 --- a/src/config/readingComplexityConfig.ts +++ /dev/null @@ -1,165 +0,0 @@ -var common_configs = { - contentLevel: [ - { - level: 'L1', - syllableCount: { $eq: 2 }, - language: 'ta', - contentType: 'Word', - }, - { - level: 'L1', - wordCount: { $gte: 2, $lte: 3 }, - language: 'ta', - contentType: 'Sentence', - }, - { - level: 'L2', - syllableCount: { $gte: 2, $lte: 3 }, - language: 'ta', - contentType: 'Word', - }, - { - level: 'L2', - wordCount: { $gte: 2, $lte: 3 }, - syllableCount: { $lte: 8 }, - syllableCountArray: { - $not: { - $elemMatch: { - v: { $gte: 4 }, - }, - }, - }, - language: 'ta', - contentType: 'Sentence', - }, - { - level: 'L3', - syllableCount: { $gte: 4 }, - language: 'ta', - contentType: 'Word', - }, - { - level: 'L3', - wordCount: { $gt: 2, $lte: 5 }, - syllableCount: { $lte: 15 }, - language: 'ta', - syllableCountArray: { - $not: { - $elemMatch: { - v: { $gte: 5 }, - }, - }, - }, - contentType: 'Sentence', - }, - { - level: 'L3', - wordCount: { $lte: 10 }, - language: 'ta', - contentType: 'Paragraph', - }, - { - level: 'L4', - wordCount: { $gt: 5, $lte: 7 }, - syllableCount: { $lte: 20 }, - language: 'ta', - syllableCountArray: { - $not: { - $elemMatch: { - v: { $gte: 7 }, - }, - }, - }, - contentType: 'Sentence', - }, - { - level: 'L4', - wordCount: { $lte: 10 }, - language: 'ta', - contentType: 'Paragraph', - }, - { - level: 'L5', - wordCount: { $gte: 7, $lte: 10 }, - language: 'ta', - contentType: 'Sentence', - }, - { - level: 'L5', - wordCount: { $gt: 10, $lte: 15 }, - language: 'ta', - contentType: 'Paragraph', - }, - { - level: 'L6', - wordCount: { $gte: 7, $lte: 12 }, - language: 'ta', - contentType: 'Sentence', - }, - { - level: 'L6', - wordCount: { $gt: 15 }, - language: 'ta', - contentType: 'Paragraph', - }, - ], - complexity: [ - { - level: 'C0', - readingComplexity : {$lt:1}, - language: 'ta', - contentType: 'Word', - }, - { - level: 'C1', - readingComplexity : { $gte :1 ,$lte:8 }, - language: 'ta', - contentType: 'Word', - }, - { - level: 'C1', - readingComplexity : { $lte:12 }, - language: 'ta', - contentType: 'Sentence', - }, - { - level: 'C2', - readingComplexity : { $lt:15 }, - language: 'ta', - contentType: 'Word', - }, - { - level: 'C2', - language: 'ta', - readingComplexity : { $lte:30 }, - contentType: 'Sentence', - }, - { - level: 'C3', - readingComplexity : { $lte:20}, - language: 'ta', - contentType: 'Word', - }, - { - level: 'C3', - readingComplexity : { $lte:50}, - language: 'ta', - contentType: 'Sentence', - }, - { - level: 'C4', - readingComplexity : { $gt:20}, - language: 'ta', - contentType: 'Word', - }, - { - level: 'C4', - readingComplexity : { $gt:50}, - language: 'ta', - contentType: 'Sentence', - }, - ], - }; - - export default common_configs; - \ No newline at end of file diff --git a/src/services/content.service.ts b/src/services/content.service.ts index fda6037..8109571 100644 --- a/src/services/content.service.ts +++ b/src/services/content.service.ts @@ -5,7 +5,6 @@ import { content, contentDocument } from '../schemas/content.schema'; import { HttpService } from '@nestjs/axios'; import en_config from 'src/config/language/en'; import common_config from 'src/config/commonConfig'; -import common_configs from 'src/config/readingComplexityConfig'; @Injectable() export class contentService { @@ -349,9 +348,8 @@ export class contentService { let prevContentLevel = ''; let contentQueryParam = []; let complexityQueryParam = []; - const configs = readingComplexityLang.includes(language) ? common_configs : common_config; - let contentLevel = configs.contentLevel; - let complexity = configs.complexity; + let contentLevel = common_config.contentLevel; + let complexity = readingComplexityLang.includes(language) ? common_config.readingComplexity : common_config.complexity ; if (cLevel != '' || complexityLevel.length != 0) { if (cLevel != 'L1') { From 61bcf292f903a6eb7d8364a0862430fe5c49e980 Mon Sep 17 00:00:00 2001 From: ViseshXX Date: Tue, 17 Jun 2025 10:54:58 +0530 Subject: [PATCH 14/22] New CEFR_level changes --- package-lock.json | 1 - src/controllers/content.controller.ts | 52 ++++++++++-------------- src/schemas/collection.schema.ts | 1 + src/schemas/content.schema.ts | 1 + src/services/collection.service.ts | 20 +++++---- src/services/content.service.ts | 58 ++++++++++++++++----------- 6 files changed, 67 insertions(+), 66 deletions(-) diff --git a/package-lock.json b/package-lock.json index 3920b99..bfd3483 100644 --- a/package-lock.json +++ b/package-lock.json @@ -8093,7 +8093,6 @@ "version": "5.5.6", "resolved": "https://registry.npmjs.org/redis/-/redis-5.5.6.tgz", "integrity": "sha512-hbpqBfcuhWHOS9YLNcXcJ4akNr7HFX61Dq3JuFZ9S7uU7C7kvnzuH2PDIXOP62A3eevvACoG8UacuXP3N07xdg==", - "license": "MIT", "dependencies": { "@redis/bloom": "5.5.6", "@redis/client": "5.5.6", diff --git a/src/controllers/content.controller.ts b/src/controllers/content.controller.ts index 222b29a..2abb0aa 100644 --- a/src/controllers/content.controller.ts +++ b/src/controllers/content.controller.ts @@ -1038,39 +1038,27 @@ export class contentController { let contentCollection; let collectionId; - if ( - queryData.story_mode === 'true' && - queryData.level_competency.length > 0 - ) { - collectionId = await this.collectionService.getCompetencyCollections( - queryData.level_competency, - queryData.language, - queryData.contentType, - ); - const contentData = await this.contentService.pagination( - 0, - parseInt(Batch), - queryData.contentType, - collectionId, - ); + if(queryData.story_mode === "true" && queryData.level_competency.length > 0) { + collectionId = await this.collectionService.getCompetencyCollections(queryData.level_competency, queryData.language, queryData.contentType, queryData.CEFR_level); + const contentData = await this.contentService.pagination(0, parseInt(Batch),queryData.contentType,collectionId); let contentArr = contentData['data']; - if (contentArr.length === 0) { - await this.contentService - .search( - queryData.tokenArr, - queryData.language, - queryData.contentType, - parseInt(Batch), - queryData.tags, - queryData.cLevel, - queryData.complexityLevel, - queryData.graphemesMappedObj, - queryData.level_competency, - ) - .then((contentData) => { - contentArr = contentData['wordsArr']; - }); + if(contentArr.length === 0){ + + await this.contentService.search( + queryData.tokenArr, + queryData.language, + queryData.contentType, + parseInt(Batch), + queryData.tags, + queryData.cLevel, + queryData.complexityLevel, + queryData.graphemesMappedObj, + queryData.level_competency, + queryData.CEFR_level + ).then((contentData)=>{ + contentArr = contentData['wordsArr']; + }); } if (queryData.mechanics_id !== undefined) { @@ -1100,6 +1088,7 @@ export class contentController { queryData.complexityLevel, queryData.graphemesMappedObj, queryData.level_competency, + queryData.CEFR_level ); } else { contentCollection = await this.contentService.getMechanicsContentData( @@ -1109,6 +1098,7 @@ export class contentController { queryData.language, queryData.level_competency, queryData.tags, + queryData.CEFR_level ); } diff --git a/src/schemas/collection.schema.ts b/src/schemas/collection.schema.ts index 4f4ca04..73ddc3a 100644 --- a/src/schemas/collection.schema.ts +++ b/src/schemas/collection.schema.ts @@ -90,6 +90,7 @@ export class collection { level_complexity: { level: string; level_competency: string; + CEFR_level?: string; }; @Prop({ required: true }) diff --git a/src/schemas/content.schema.ts b/src/schemas/content.schema.ts index 98158c3..f3a8c6e 100644 --- a/src/schemas/content.schema.ts +++ b/src/schemas/content.schema.ts @@ -75,6 +75,7 @@ export class content { level_complexity: { level: string; level_competency: string; + CEFR_level?: string; }; @Prop({ type: String, required: false }) diff --git a/src/services/collection.service.ts b/src/services/collection.service.ts index e0d1a95..1c190e5 100644 --- a/src/services/collection.service.ts +++ b/src/services/collection.service.ts @@ -66,18 +66,16 @@ export class CollectionService { }; } - async getCompetencyCollections( - level_competency = [], - language = 'en', - contentType, - ): Promise { + async getCompetencyCollections(level_competency = [], language = "en", contentType, CEFR_level = []): Promise { + let matchQuery = { + "level_complexity.level_competency": {$in: level_competency}, + "language": language, + "category": contentType, + ...(CEFR_level.length > 0 && { "level_complexity.CEFR_level": {$in: CEFR_level} }) + }; let collectionIds = await this.collectionModel.aggregate([ - { - $match: { - 'level_complexity.level_competency': { $in: level_competency }, - language: language, - category: contentType, - }, + { + $match: matchQuery }, { $sample: { size: 1 }, diff --git a/src/services/content.service.ts b/src/services/content.service.ts index 8109571..8b54a5c 100644 --- a/src/services/content.service.ts +++ b/src/services/content.service.ts @@ -332,6 +332,7 @@ export class contentService { complexityLevel, graphemesMappedObj, level_competency = [], + CEFR_level = [] ): Promise { let nextTokenArr = []; let readingComplexityLang =['hi']; @@ -1120,6 +1121,9 @@ export class contentService { query['level_complexity.level_competency'] = { $in: level_competency }; } + if (CEFR_level?.length > 0) { + query["level_complexity.CEFR_level"] = {$in:CEFR_level}; + } const allTokenGraphemes = []; let contentData = []; @@ -1332,7 +1336,8 @@ export class contentService { $elemMatch: {}, }, contentType: contentType, - 'level_complexity.level_competency': { $in: level_competency }, + "level_complexity.level_competency": {$in:level_competency}, + ...(CEFR_level.length > 0 && { "level_complexity.CEFR_level": { $in: CEFR_level } }) }; randomContentQuery.contentSourceData.$elemMatch['language'] = @@ -1957,6 +1962,7 @@ export class contentService { language, levelCompetencyArr, tags, + CEFR_level = [] ) { let queries = []; const numLevels = levelCompetencyArr.length; @@ -1981,6 +1987,7 @@ export class contentService { }, }, 'level_complexity.level_competency': levelCompetency, + ...(CEFR_level.length > 0 && { "level_complexity.CEFR_level": { $in: CEFR_level } }), tags: { $all: tags }, }, }, @@ -2000,8 +2007,9 @@ export class contentService { language: language, }, }, - 'level_complexity.level_competency': levelCompetency, - }, + "level_complexity.level_competency": levelCompetency, + ...(CEFR_level.length > 0 && { "level_complexity.CEFR_level": { $in: CEFR_level } }) + } }, { $sample: { size: splitLimit } }, // Fetch for the first level ]), @@ -2024,6 +2032,7 @@ export class contentService { }, }, 'level_complexity.level_competency': levelCompetency, + ...(CEFR_level.length > 0 && { "level_complexity.CEFR_level": { $in: CEFR_level } }), tags: { $all: tags }, }, }, @@ -2043,8 +2052,9 @@ export class contentService { language: language, }, }, - 'level_complexity.level_competency': levelCompetency, - }, + "level_complexity.level_competency": levelCompetency, + ...(CEFR_level.length > 0 && { "level_complexity.CEFR_level": { $in: CEFR_level } }) + } }, { $sample: { size: splitLimit - handleLimit } }, // Fetch fewer items for other levels ]), @@ -2073,22 +2083,23 @@ export class contentService { if (results.length < limit) { const remainingLimit = limit - results.length; let additionalContent; - if (tags.length > 0) { - additionalContent = await this.content.aggregate([ - { - $match: { - contentType: contentType, - language: language, - mechanics_data: { - $elemMatch: { mechanics_id: mechanics_id, language: language }, - }, - 'level_complexity.level_competency': { $exists: true }, - tags: { $all: tags }, + if(tags.length > 0){ + additionalContent= await this.content.aggregate([ + { + $match: { + contentType: contentType, + language: language, + mechanics_data: { + $elemMatch: { mechanics_id: mechanics_id, language: language } }, - }, - { $sample: { size: remainingLimit } }, - ]); - } else { + "level_complexity.level_competency": { $exists: true }, + ...(CEFR_level.length > 0 && { "level_complexity.CEFR_level": { $in: CEFR_level } }), + "tags":{ $all: tags } + } + }, + { $sample: { size: remainingLimit } } + ]); + }else{ additionalContent = await this.content.aggregate([ { $match: { @@ -2097,10 +2108,11 @@ export class contentService { mechanics_data: { $elemMatch: { mechanics_id: mechanics_id, language: language }, }, - 'level_complexity.level_competency': { $exists: true }, - }, + "level_complexity.level_competency": { $exists: true }, + ...(CEFR_level.length > 0 && { "level_complexity.CEFR_level": { $in: CEFR_level } }) + } }, - { $sample: { size: remainingLimit } }, + { $sample: { size: remainingLimit } } ]); } From 47d5c8a805919cbe47a718520b16efa850f0a66d Mon Sep 17 00:00:00 2001 From: ViseshXX Date: Tue, 17 Jun 2025 12:17:27 +0530 Subject: [PATCH 15/22] Format changes --- src/auth/auth.guard.ts | 4 +- src/config/commonConfig.ts | 18 ++--- src/controllers/content.controller.ts | 56 +++++++++------ src/services/collection.service.ts | 21 ++++-- src/services/content.service.ts | 98 +++++++++++++++------------ 5 files changed, 116 insertions(+), 81 deletions(-) diff --git a/src/auth/auth.guard.ts b/src/auth/auth.guard.ts index 3d0414e..9849c2a 100644 --- a/src/auth/auth.guard.ts +++ b/src/auth/auth.guard.ts @@ -13,7 +13,8 @@ import { RedisClientType } from 'redis'; @Injectable() export class JwtAuthGuard implements CanActivate { - constructor(private jwtService: JwtService, + constructor( + private jwtService: JwtService, @Inject('REDIS_CLIENT') private readonly redisClient: RedisClientType, ) {} @@ -35,7 +36,6 @@ export class JwtAuthGuard implements CanActivate { const secret_key = process.env.JOSE_SECRET || ''; const hash = createHash('sha256').update(secret_key).digest(); - //Step 2: Decrypt the Token const jwtDecryptedToken = await jose.jwtDecrypt(token, hash); diff --git a/src/config/commonConfig.ts b/src/config/commonConfig.ts index 8f492a4..435544a 100644 --- a/src/config/commonConfig.ts +++ b/src/config/commonConfig.ts @@ -168,55 +168,55 @@ var common_config = { readingComplexity: [ { level: 'C0', - readingComplexity : {$lt:1}, + readingComplexity: { $lt: 1 }, language: 'ta', contentType: 'Word', }, { level: 'C1', - readingComplexity : { $gte :1 ,$lte:8 }, + readingComplexity: { $gte: 1, $lte: 8 }, language: 'ta', contentType: 'Word', }, { level: 'C1', - readingComplexity : { $lte:12 }, + readingComplexity: { $lte: 12 }, language: 'ta', contentType: 'Sentence', }, { level: 'C2', - readingComplexity : { $lt:15 }, + readingComplexity: { $lt: 15 }, language: 'ta', contentType: 'Word', }, { level: 'C2', language: 'ta', - readingComplexity : { $lte:30 }, + readingComplexity: { $lte: 30 }, contentType: 'Sentence', }, { level: 'C3', - readingComplexity : { $lte:20}, + readingComplexity: { $lte: 20 }, language: 'ta', contentType: 'Word', }, { level: 'C3', - readingComplexity : { $lte:50}, + readingComplexity: { $lte: 50 }, language: 'ta', contentType: 'Sentence', }, { level: 'C4', - readingComplexity : { $gt:20}, + readingComplexity: { $gt: 20 }, language: 'ta', contentType: 'Word', }, { level: 'C4', - readingComplexity : { $gt:50}, + readingComplexity: { $gt: 50 }, language: 'ta', contentType: 'Sentence', }, diff --git a/src/controllers/content.controller.ts b/src/controllers/content.controller.ts index 2abb0aa..fc74c95 100644 --- a/src/controllers/content.controller.ts +++ b/src/controllers/content.controller.ts @@ -1038,27 +1038,41 @@ export class contentController { let contentCollection; let collectionId; - if(queryData.story_mode === "true" && queryData.level_competency.length > 0) { - collectionId = await this.collectionService.getCompetencyCollections(queryData.level_competency, queryData.language, queryData.contentType, queryData.CEFR_level); - const contentData = await this.contentService.pagination(0, parseInt(Batch),queryData.contentType,collectionId); + if ( + queryData.story_mode === 'true' && + queryData.level_competency.length > 0 + ) { + collectionId = await this.collectionService.getCompetencyCollections( + queryData.level_competency, + queryData.language, + queryData.contentType, + queryData.CEFR_level, + ); + const contentData = await this.contentService.pagination( + 0, + parseInt(Batch), + queryData.contentType, + collectionId, + ); let contentArr = contentData['data']; - if(contentArr.length === 0){ - - await this.contentService.search( - queryData.tokenArr, - queryData.language, - queryData.contentType, - parseInt(Batch), - queryData.tags, - queryData.cLevel, - queryData.complexityLevel, - queryData.graphemesMappedObj, - queryData.level_competency, - queryData.CEFR_level - ).then((contentData)=>{ - contentArr = contentData['wordsArr']; - }); + if (contentArr.length === 0) { + await this.contentService + .search( + queryData.tokenArr, + queryData.language, + queryData.contentType, + parseInt(Batch), + queryData.tags, + queryData.cLevel, + queryData.complexityLevel, + queryData.graphemesMappedObj, + queryData.level_competency, + queryData.CEFR_level, + ) + .then((contentData) => { + contentArr = contentData['wordsArr']; + }); } if (queryData.mechanics_id !== undefined) { @@ -1088,7 +1102,7 @@ export class contentController { queryData.complexityLevel, queryData.graphemesMappedObj, queryData.level_competency, - queryData.CEFR_level + queryData.CEFR_level, ); } else { contentCollection = await this.contentService.getMechanicsContentData( @@ -1098,7 +1112,7 @@ export class contentController { queryData.language, queryData.level_competency, queryData.tags, - queryData.CEFR_level + queryData.CEFR_level, ); } diff --git a/src/services/collection.service.ts b/src/services/collection.service.ts index 1c190e5..3dcb89d 100644 --- a/src/services/collection.service.ts +++ b/src/services/collection.service.ts @@ -66,16 +66,23 @@ export class CollectionService { }; } - async getCompetencyCollections(level_competency = [], language = "en", contentType, CEFR_level = []): Promise { + async getCompetencyCollections( + level_competency = [], + language = 'en', + contentType, + CEFR_level = [], + ): Promise { let matchQuery = { - "level_complexity.level_competency": {$in: level_competency}, - "language": language, - "category": contentType, - ...(CEFR_level.length > 0 && { "level_complexity.CEFR_level": {$in: CEFR_level} }) + 'level_complexity.level_competency': { $in: level_competency }, + language: language, + category: contentType, + ...(CEFR_level.length > 0 && { + 'level_complexity.CEFR_level': { $in: CEFR_level }, + }), }; let collectionIds = await this.collectionModel.aggregate([ - { - $match: matchQuery + { + $match: matchQuery, }, { $sample: { size: 1 }, diff --git a/src/services/content.service.ts b/src/services/content.service.ts index 8b54a5c..e273599 100644 --- a/src/services/content.service.ts +++ b/src/services/content.service.ts @@ -332,10 +332,10 @@ export class contentService { complexityLevel, graphemesMappedObj, level_competency = [], - CEFR_level = [] + CEFR_level = [], ): Promise { let nextTokenArr = []; - let readingComplexityLang =['hi']; + let readingComplexityLang = ['hi']; if (tokenArr.length >= limit * 2) { nextTokenArr = tokenArr.slice(limit, limit * 2); } else { @@ -350,7 +350,9 @@ export class contentService { let contentQueryParam = []; let complexityQueryParam = []; let contentLevel = common_config.contentLevel; - let complexity = readingComplexityLang.includes(language) ? common_config.readingComplexity : common_config.complexity ; + let complexity = readingComplexityLang.includes(language) + ? common_config.readingComplexity + : common_config.complexity; if (cLevel != '' || complexityLevel.length != 0) { if (cLevel != 'L1') { @@ -390,20 +392,18 @@ export class contentService { delete complexityQueryParamEle.level; delete complexityQueryParamEle.contentType; delete complexityQueryParamEle.language; - if(readingComplexityLang.includes(language)) { + if (readingComplexityLang.includes(language)) { mileStoneQuery.push({ - readingComplexity: - complexityQueryParamEle.readingComplexity, + readingComplexity: complexityQueryParamEle.readingComplexity, }); - - } - else { + } else { mileStoneQuery.push({ totalPhonicComplexity: complexityQueryParamEle.totalPhonicComplexity, }); mileStoneQuery.push({ - totalOrthoComplexity: complexityQueryParamEle.totalOrthoComplexity, + totalOrthoComplexity: + complexityQueryParamEle.totalOrthoComplexity, }); } } @@ -1122,7 +1122,7 @@ export class contentService { } if (CEFR_level?.length > 0) { - query["level_complexity.CEFR_level"] = {$in:CEFR_level}; + query['level_complexity.CEFR_level'] = { $in: CEFR_level }; } const allTokenGraphemes = []; @@ -1336,8 +1336,10 @@ export class contentService { $elemMatch: {}, }, contentType: contentType, - "level_complexity.level_competency": {$in:level_competency}, - ...(CEFR_level.length > 0 && { "level_complexity.CEFR_level": { $in: CEFR_level } }) + 'level_complexity.level_competency': { $in: level_competency }, + ...(CEFR_level.length > 0 && { + 'level_complexity.CEFR_level': { $in: CEFR_level }, + }), }; randomContentQuery.contentSourceData.$elemMatch['language'] = @@ -1962,7 +1964,7 @@ export class contentService { language, levelCompetencyArr, tags, - CEFR_level = [] + CEFR_level = [], ) { let queries = []; const numLevels = levelCompetencyArr.length; @@ -1987,7 +1989,9 @@ export class contentService { }, }, 'level_complexity.level_competency': levelCompetency, - ...(CEFR_level.length > 0 && { "level_complexity.CEFR_level": { $in: CEFR_level } }), + ...(CEFR_level.length > 0 && { + 'level_complexity.CEFR_level': { $in: CEFR_level }, + }), tags: { $all: tags }, }, }, @@ -2007,9 +2011,11 @@ export class contentService { language: language, }, }, - "level_complexity.level_competency": levelCompetency, - ...(CEFR_level.length > 0 && { "level_complexity.CEFR_level": { $in: CEFR_level } }) - } + 'level_complexity.level_competency': levelCompetency, + ...(CEFR_level.length > 0 && { + 'level_complexity.CEFR_level': { $in: CEFR_level }, + }), + }, }, { $sample: { size: splitLimit } }, // Fetch for the first level ]), @@ -2032,7 +2038,9 @@ export class contentService { }, }, 'level_complexity.level_competency': levelCompetency, - ...(CEFR_level.length > 0 && { "level_complexity.CEFR_level": { $in: CEFR_level } }), + ...(CEFR_level.length > 0 && { + 'level_complexity.CEFR_level': { $in: CEFR_level }, + }), tags: { $all: tags }, }, }, @@ -2052,9 +2060,11 @@ export class contentService { language: language, }, }, - "level_complexity.level_competency": levelCompetency, - ...(CEFR_level.length > 0 && { "level_complexity.CEFR_level": { $in: CEFR_level } }) - } + 'level_complexity.level_competency': levelCompetency, + ...(CEFR_level.length > 0 && { + 'level_complexity.CEFR_level': { $in: CEFR_level }, + }), + }, }, { $sample: { size: splitLimit - handleLimit } }, // Fetch fewer items for other levels ]), @@ -2083,23 +2093,25 @@ export class contentService { if (results.length < limit) { const remainingLimit = limit - results.length; let additionalContent; - if(tags.length > 0){ - additionalContent= await this.content.aggregate([ - { - $match: { - contentType: contentType, - language: language, - mechanics_data: { - $elemMatch: { mechanics_id: mechanics_id, language: language } + if (tags.length > 0) { + additionalContent = await this.content.aggregate([ + { + $match: { + contentType: contentType, + language: language, + mechanics_data: { + $elemMatch: { mechanics_id: mechanics_id, language: language }, + }, + 'level_complexity.level_competency': { $exists: true }, + ...(CEFR_level.length > 0 && { + 'level_complexity.CEFR_level': { $in: CEFR_level }, + }), + tags: { $all: tags }, }, - "level_complexity.level_competency": { $exists: true }, - ...(CEFR_level.length > 0 && { "level_complexity.CEFR_level": { $in: CEFR_level } }), - "tags":{ $all: tags } - } - }, - { $sample: { size: remainingLimit } } - ]); - }else{ + }, + { $sample: { size: remainingLimit } }, + ]); + } else { additionalContent = await this.content.aggregate([ { $match: { @@ -2108,11 +2120,13 @@ export class contentService { mechanics_data: { $elemMatch: { mechanics_id: mechanics_id, language: language }, }, - "level_complexity.level_competency": { $exists: true }, - ...(CEFR_level.length > 0 && { "level_complexity.CEFR_level": { $in: CEFR_level } }) - } + 'level_complexity.level_competency': { $exists: true }, + ...(CEFR_level.length > 0 && { + 'level_complexity.CEFR_level': { $in: CEFR_level }, + }), + }, }, - { $sample: { size: remainingLimit } } + { $sample: { size: remainingLimit } }, ]); } From bc36d4ed6f1f486b7c88c0c5f77813aacbad3e1a Mon Sep 17 00:00:00 2001 From: DevendraPPatil Date: Fri, 20 Jun 2025 11:43:18 +0530 Subject: [PATCH 16/22] Fix- Security policy added --- src/main.ts | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/src/main.ts b/src/main.ts index 5d7b63b..4a9735b 100644 --- a/src/main.ts +++ b/src/main.ts @@ -19,12 +19,7 @@ async function bootstrap() { app.enableCors({ origin: ['*'], methods: ['GET', 'POST', 'HEAD', 'PUT', 'PATCH', 'DELETE', 'OPTIONS'], - credentials: false, - exposedHeaders: [ - 'X-Content-Type-Options', - 'X-Frame-Options', - 'X-XSS-Protection', - ], + credentials: false }); const config = new DocumentBuilder() @@ -46,6 +41,7 @@ async function bootstrap() { reply.header('X-Content-Type-Options', 'nosniff'); reply.header('X-Frame-Options', 'DENY'); reply.header('Content-Security-Policy', "default-src 'self'"); + reply.header('X-XSS-Protection', '1; mode=block'); return payload; }); From 51fc0c798259632461b7d39374a3ca1ef3a6623d Mon Sep 17 00:00:00 2001 From: DevendraPPatil Date: Fri, 20 Jun 2025 12:51:21 +0530 Subject: [PATCH 17/22] Fix : cors origin added for the specific apis --- package-lock.json | 2 ++ package.json | 1 + src/main.ts | 19 ++++++++++++++++++- 3 files changed, 21 insertions(+), 1 deletion(-) diff --git a/package-lock.json b/package-lock.json index 3920b99..4d0d169 100644 --- a/package-lock.json +++ b/package-lock.json @@ -25,6 +25,7 @@ "axios": "^1.6.2", "class-transformer": "^0.5.1", "class-validator": "^0.14.0", + "cors": "^2.8.5", "dotenv": "^16.3.1", "express": "^4.18.2", "fastify": "^4.19.2", @@ -3979,6 +3980,7 @@ "version": "2.8.5", "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.5.tgz", "integrity": "sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==", + "license": "MIT", "dependencies": { "object-assign": "^4", "vary": "^1" diff --git a/package.json b/package.json index de6d317..b2a613d 100644 --- a/package.json +++ b/package.json @@ -36,6 +36,7 @@ "axios": "^1.6.2", "class-transformer": "^0.5.1", "class-validator": "^0.14.0", + "cors": "^2.8.5", "dotenv": "^16.3.1", "express": "^4.18.2", "fastify": "^4.19.2", diff --git a/src/main.ts b/src/main.ts index 4a9735b..d25483c 100644 --- a/src/main.ts +++ b/src/main.ts @@ -7,6 +7,8 @@ import { AppModule } from './app.module'; import { ValidationPipe } from '@nestjs/common'; import { SwaggerModule, DocumentBuilder } from '@nestjs/swagger'; import { AppClusterService } from './app-cluster.service'; +import * as cors from 'cors'; + async function bootstrap() { const app = await NestFactory.create( @@ -17,7 +19,6 @@ async function bootstrap() { app.useGlobalPipes(new ValidationPipe()); app.setGlobalPrefix('v1'); app.enableCors({ - origin: ['*'], methods: ['GET', 'POST', 'HEAD', 'PUT', 'PATCH', 'DELETE', 'OPTIONS'], credentials: false }); @@ -34,6 +35,22 @@ async function bootstrap() { const document = SwaggerModule.createDocument(app, config); SwaggerModule.setup('api', app, document); + // Cors aalowd for the specific url + const allowedOrigins = process.env.ALLOWED_ORIGINS?.split(',') || []; + app.use( + cors({ + origin: (origin, callback) => { + if (!origin || allowedOrigins.includes(origin)) { + callback(null, true); + } else { + callback(new Error('Not allowed by CORS')); + } + }, + credentials: true, + }), + ); + + // content-security-policys added app .getHttpAdapter() .getInstance() From 44f6f3e2d5e72f174789399dc617644fdc92c3bb Mon Sep 17 00:00:00 2001 From: Ishan-ttpl Date: Fri, 20 Jun 2025 15:00:03 +0530 Subject: [PATCH 18/22] Delete deploy.sh --- deploy.sh | 28 ---------------------------- 1 file changed, 28 deletions(-) delete mode 100644 deploy.sh diff --git a/deploy.sh b/deploy.sh deleted file mode 100644 index 324f486..0000000 --- a/deploy.sh +++ /dev/null @@ -1,28 +0,0 @@ -#!/bin/bash -set -e -TARGET_DIR="$1" -REF="$2" # Branch for deploy, tag for rollback -ACTION="$3" # deploy or rollback - -cd "$TARGET_DIR" || { echo "Error: Directory $TARGET_DIR not found."; exit 1; } -echo "Fetching all latest changes..." -git fetch --all -if [ "$ACTION" = "deploy" ]; then - echo "Checking out branch: $REF..." - git checkout "$REF" || { echo "Error: Failed to checkout branch $REF."; exit 1; } - echo "Pulling latest changes for $REF..." - git pull origin "$REF" || { echo "Error: Failed to pull from origin $REF."; exit 1; } -elif [ "$ACTION" = "rollback" ]; then - echo "Checking out tag: $REF..." - git checkout "tags/$REF" || { echo "Error: Failed to checkout tag $REF."; exit 1; } -else - echo "Error: Invalid action $ACTION. Expected 'deploy' or 'rollback'." - exit 1 -fi -echo "Stopping existing Docker containers..." -docker-compose down || echo "No running containers to stop." -echo "Building and starting new Docker containers..." -docker-compose up -d --build || { echo "Error: Docker compose failed."; exit 1; } -echo "Cleaning dangling Docker images..." -docker images --no-trunc -aqf "dangling=true" | xargs docker rmi || echo "No dangling images." -echo "$ACTION complete for $REF." From a12fec64d6cdcf853f7901dbd962c3bcd78e7eab Mon Sep 17 00:00:00 2001 From: Ishan-ttpl Date: Fri, 20 Jun 2025 15:00:24 +0530 Subject: [PATCH 19/22] Delete .github/workflows/deploy.yml --- .github/workflows/deploy.yml | 82 ------------------------------------ 1 file changed, 82 deletions(-) delete mode 100644 .github/workflows/deploy.yml diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml deleted file mode 100644 index bf0bfec..0000000 --- a/.github/workflows/deploy.yml +++ /dev/null @@ -1,82 +0,0 @@ -name: Deploy or Rollback All-Content-Service - -on: - push: - tags: - - 'deploy-tn-staging-*' - workflow_dispatch: - inputs: - action: - description: 'Action to perform' - required: true - default: 'deploy' - type: choice - options: - - deploy - - rollback - environment: - description: 'Target environment' - required: true - default: 'tn-staging' - type: choice - options: - - tn-staging - branch: - description: 'Branch to deploy (only for "deploy" action)' - required: false - default: 'lais-v2.0' - rollback_tag: - description: 'Tag to rollback to (only for "rollback" action, e.g., deploy-tn-staging-v1.0.0)' - required: false - -jobs: - deploy-or-rollback: - runs-on: ubuntu-latest - environment: tn-staging - if: github.event_name == 'push' || github.event_name == 'workflow_dispatch' - - steps: - - name: Checkout repository - uses: actions/checkout@v4 - with: - fetch-depth: 0 - ref: ${{ github.event.inputs.branch || 'lais-v2.0' }} - - - name: Set SSH Key and Target Host - run: | - echo "${{ secrets.SSH_KEY }}" > key.pem - echo "${{ secrets.SSH_KEY }}" > jump_key.pem - chmod 600 key.pem jump_key.pem - echo "host=${{ secrets.TN_STAGING_HOST }}" >> "$GITHUB_ENV" - echo "target_dir=/home/all-staging-user/all-services/all-content-deploy" >> "$GITHUB_ENV" - echo "jump_host=${{ secrets.JUMPHOST }}" >> "$GITHUB_ENV" - - - name: Perform Deploy or Rollback - run: | - ACTION="${{ github.event.inputs.action || 'deploy' }}" - HOST="${{ env.host }}" - TARGET_DIR="${{ env.target_dir }}" - JUMP_HOST="${{ env.jump_host }}" - - SSH_COMMAND="ssh -o StrictHostKeyChecking=no -i jump_key.pem -J $JUMP_HOST -i key.pem $HOST" - - if [[ "$ACTION" == "deploy" ]]; then - BRANCH="${{ github.event.inputs.branch || 'lais-v2.0' }}" - echo "Initiating deployment to $HOST from branch: $BRANCH" - $SSH_COMMAND "cd $TARGET_DIR && \ - chmod +x deploy.sh && \ - ./deploy.sh $TARGET_DIR $BRANCH deploy" - elif [[ "$ACTION" == "rollback" ]]; then - ROLLBACK_TAG="${{ github.event.inputs.rollback_tag }}" - if [[ -z "$ROLLBACK_TAG" ]]; then - echo "::error::Rollback tag is required for rollback action." - exit 1 - fi - echo "Initiating rollback to tag: $ROLLBACK_TAG on $HOST" - $SSH_COMMAND "cd $TARGET_DIR && \ - chmod +x deploy.sh && \ - ./deploy.sh $TARGET_DIR $ROLLBACK_TAG rollback" - else - echo "::error::Invalid action specified: $ACTION" - exit 1 - fi From 05f56490b3db63afd1d933f57d31d10891454e98 Mon Sep 17 00:00:00 2001 From: DevendraPPatil Date: Mon, 23 Jun 2025 15:31:56 +0530 Subject: [PATCH 20/22] Fix : redis removed --- package-lock.json | 1 - package.json | 1 - src/app.module.ts | 4 +--- src/auth/auth.guard.ts | 45 ++++++++++++++++++++++++++++----------- src/redis/redis.module.ts | 24 --------------------- 5 files changed, 33 insertions(+), 42 deletions(-) delete mode 100644 src/redis/redis.module.ts diff --git a/package-lock.json b/package-lock.json index 5b29a4a..1a2f6b5 100644 --- a/package-lock.json +++ b/package-lock.json @@ -34,7 +34,6 @@ "jsonwebtoken": "^9.0.2", "mongoose": "^7.3.2", "passport-jwt": "^4.0.1", - "redis": "^5.5.6", "reflect-metadata": "^0.1.13", "rxjs": "^7.8.1", "split-graphemes": "^0.5.0", diff --git a/package.json b/package.json index b2a613d..7d798f2 100644 --- a/package.json +++ b/package.json @@ -45,7 +45,6 @@ "jsonwebtoken": "^9.0.2", "mongoose": "^7.3.2", "passport-jwt": "^4.0.1", - "redis": "^5.5.6", "reflect-metadata": "^0.1.13", "rxjs": "^7.8.1", "split-graphemes": "^0.5.0", diff --git a/src/app.module.ts b/src/app.module.ts index a658caf..75d2ac3 100644 --- a/src/app.module.ts +++ b/src/app.module.ts @@ -11,7 +11,6 @@ import { CollectionController } from './controllers/collection.controller'; import { CollectionService } from './services/collection.service'; import { HttpModule } from '@nestjs/axios'; import { AuthModule } from './auth/auth.module'; -import { RedisModule } from './redis/redis.module'; @Module({ imports: [ @@ -38,8 +37,7 @@ import { RedisModule } from './redis/redis.module'; { name: content.name, schema: contentSchema }, { name: collection.name, schema: collectionDbSchema }, ]), - AuthModule, - RedisModule, + AuthModule ], controllers: [AppController, contentController, CollectionController], providers: [AppService, contentService, CollectionService], diff --git a/src/auth/auth.guard.ts b/src/auth/auth.guard.ts index 9849c2a..5c44c36 100644 --- a/src/auth/auth.guard.ts +++ b/src/auth/auth.guard.ts @@ -6,17 +6,17 @@ import { Inject, } from '@nestjs/common'; import { JwtService } from '@nestjs/jwt'; +import axios from 'axios'; import { createHash } from 'crypto'; import { Request } from 'express'; import * as jose from 'jose'; -import { RedisClientType } from 'redis'; + @Injectable() export class JwtAuthGuard implements CanActivate { constructor( - private jwtService: JwtService, - @Inject('REDIS_CLIENT') private readonly redisClient: RedisClientType, - ) {} + private jwtService: JwtService + ) { } async canActivate(context: ExecutionContext): Promise { const request = context.switchToHttp().getRequest(); @@ -25,13 +25,8 @@ export class JwtAuthGuard implements CanActivate { throw new UnauthorizedException('Authorization header missing'); } const token = authHeader.split(' ')[1]; + try { - // Check Redis Blacklist - const isBlacklisted = await this.redisClient.get(`blacklist:${token}`); - if (isBlacklisted) { - throw new UnauthorizedException('Token has been logged out'); - } - //Step 1: Correctly Generate Encryption Key const secret_key = process.env.JOSE_SECRET || ''; const hash = createHash('sha256').update(secret_key).digest(); @@ -45,13 +40,17 @@ export class JwtAuthGuard implements CanActivate { //Step 3: Verify the Signed JWT const jwtSignedToken = String(jwtDecryptedToken.payload.jwtSignedToken); - - //Fix Signing Key const jwtSigninKey = new TextEncoder().encode( process.env.JWT_SIGNIN_PRIVATE_KEY, ); const verifiedToken = await jose.jwtVerify(jwtSignedToken, jwtSigninKey); - + + // get the token status + const tokenStatus = await this.checkTokenStatus(verifiedToken.payload.virtual_id); + if (!tokenStatus.isLoggedIn) { + throw new UnauthorizedException('User is logged out'); + } + //Step 4: Attach User Data to Request (request as any).user = verifiedToken.payload; @@ -60,4 +59,24 @@ export class JwtAuthGuard implements CanActivate { throw new UnauthorizedException('Invalid or expired token'); } } + + // check user status + async checkTokenStatus(user_id: any): Promise<{ isLoggedIn: boolean }> { + try { + const url = process.env.ALL_ORC_SERVICE_URL; + const response = await axios.post(url, { + user_id: user_id, + }); + + return { + isLoggedIn: response.data?.result?.isLoggedIn ?? false + }; + } catch (error: any) { + console.error('Error calling token-status API:', error?.response?.data || error.message); + return { + isLoggedIn: false, + }; + } + } + } diff --git a/src/redis/redis.module.ts b/src/redis/redis.module.ts deleted file mode 100644 index cfdb00b..0000000 --- a/src/redis/redis.module.ts +++ /dev/null @@ -1,24 +0,0 @@ -import { Module, Global } from '@nestjs/common'; -import { createClient } from 'redis'; - -const redisClient = createClient({ - socket: { - host: process.env.REDIS_HOST || '127.0.0.1', - port: parseInt(process.env.REDIS_PORT || '6379'), - }, - password: process.env.REDIS_PASSWORD, -}); - -redisClient.connect().catch(console.error); - -@Global() -@Module({ - providers: [ - { - provide: 'REDIS_CLIENT', - useValue: redisClient, - }, - ], - exports: ['REDIS_CLIENT'], -}) -export class RedisModule {} From 0998e3d6218138258abd4df5a6985dfa907431b9 Mon Sep 17 00:00:00 2001 From: vishnu vinay Date: Mon, 23 Jun 2025 21:03:40 +0530 Subject: [PATCH 21/22] Task : Token validation changes --- src/auth/auth.guard.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/auth/auth.guard.ts b/src/auth/auth.guard.ts index 5c44c36..4849194 100644 --- a/src/auth/auth.guard.ts +++ b/src/auth/auth.guard.ts @@ -47,7 +47,7 @@ export class JwtAuthGuard implements CanActivate { // get the token status const tokenStatus = await this.checkTokenStatus(verifiedToken.payload.virtual_id); - if (!tokenStatus.isLoggedIn) { + if (tokenStatus.token == null || tokenStatus.token !== token) { throw new UnauthorizedException('User is logged out'); } @@ -61,7 +61,7 @@ export class JwtAuthGuard implements CanActivate { } // check user status - async checkTokenStatus(user_id: any): Promise<{ isLoggedIn: boolean }> { + async checkTokenStatus(user_id: any): Promise<{ token: string }> { try { const url = process.env.ALL_ORC_SERVICE_URL; const response = await axios.post(url, { @@ -69,12 +69,12 @@ export class JwtAuthGuard implements CanActivate { }); return { - isLoggedIn: response.data?.result?.isLoggedIn ?? false + token: response.data?.result?.token || null, }; } catch (error: any) { console.error('Error calling token-status API:', error?.response?.data || error.message); return { - isLoggedIn: false, + token: null, }; } } From dc25873186cd24d2039d043fd316da95695e69a2 Mon Sep 17 00:00:00 2001 From: DevendraPPatil Date: Tue, 24 Jun 2025 19:07:40 +0530 Subject: [PATCH 22/22] Fix :credential changes added --- src/main.ts | 30 +++++++++++++----------------- 1 file changed, 13 insertions(+), 17 deletions(-) diff --git a/src/main.ts b/src/main.ts index d25483c..d07216f 100644 --- a/src/main.ts +++ b/src/main.ts @@ -18,11 +18,7 @@ async function bootstrap() { app.useGlobalPipes(new ValidationPipe()); app.setGlobalPrefix('v1'); - app.enableCors({ - methods: ['GET', 'POST', 'HEAD', 'PUT', 'PATCH', 'DELETE', 'OPTIONS'], - credentials: false - }); - + const config = new DocumentBuilder() .setTitle('ALL Content Service') .setDescription( @@ -37,18 +33,18 @@ async function bootstrap() { // Cors aalowd for the specific url const allowedOrigins = process.env.ALLOWED_ORIGINS?.split(',') || []; - app.use( - cors({ - origin: (origin, callback) => { - if (!origin || allowedOrigins.includes(origin)) { - callback(null, true); - } else { - callback(new Error('Not allowed by CORS')); - } - }, - credentials: true, - }), - ); + app.enableCors({ + origin: (origin, callback) => { + if (!origin || allowedOrigins.includes(origin)) { + callback(null, true); + } else { + callback(new Error('Not allowed by CORS')); + } + }, + methods: ['GET', 'POST', 'HEAD', 'PUT', 'PATCH', 'DELETE', 'OPTIONS'], + credentials: true, + }); + // content-security-policys added app