From 7de8823c94bb50399de23c9687a22065679a7922 Mon Sep 17 00:00:00 2001 From: venushathi <148311558+vthish@users.noreply.github.com> Date: Wed, 17 Jun 2026 18:30:40 +0530 Subject: [PATCH 01/12] Update AWS S3 configurations and presigned URL logic --- backend/nest-api/package-lock.json | 681 ++++++++++++++++++ backend/nest-api/package.json | 2 + .../src/license/license.controller.ts | 8 + .../nest-api/src/license/license.service.ts | 38 +- 4 files changed, 727 insertions(+), 2 deletions(-) diff --git a/backend/nest-api/package-lock.json b/backend/nest-api/package-lock.json index 3cd622d..a4ed7b9 100644 --- a/backend/nest-api/package-lock.json +++ b/backend/nest-api/package-lock.json @@ -9,6 +9,8 @@ "version": "0.0.1", "license": "UNLICENSED", "dependencies": { + "@aws-sdk/client-s3": "^3.1070.0", + "@aws-sdk/s3-request-presigner": "^3.1070.0", "@nestjs/axios": "^4.0.1", "@nestjs/common": "^11.0.1", "@nestjs/core": "^11.0.1", @@ -200,6 +202,453 @@ "tslib": "^2.1.0" } }, + "node_modules/@aws-crypto/crc32": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/@aws-crypto/crc32/-/crc32-5.2.0.tgz", + "integrity": "sha512-nLbCWqQNgUiwwtFsen1AdzAtvuLRsQS8rYgMuxCrdKf9kOssamGLuPwyTY9wyYblNr9+1XM8v6zoDTPPSIeANg==", + "license": "Apache-2.0", + "dependencies": { + "@aws-crypto/util": "^5.2.0", + "@aws-sdk/types": "^3.222.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-crypto/crc32c": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/@aws-crypto/crc32c/-/crc32c-5.2.0.tgz", + "integrity": "sha512-+iWb8qaHLYKrNvGRbiYRHSdKRWhto5XlZUEBwDjYNf+ly5SVYG6zEoYIdxvf5R3zyeP16w4PLBn3rH1xc74Rag==", + "license": "Apache-2.0", + "dependencies": { + "@aws-crypto/util": "^5.2.0", + "@aws-sdk/types": "^3.222.0", + "tslib": "^2.6.2" + } + }, + "node_modules/@aws-crypto/sha1-browser": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/@aws-crypto/sha1-browser/-/sha1-browser-5.2.0.tgz", + "integrity": "sha512-OH6lveCFfcDjX4dbAvCFSYUjJZjDr/3XJ3xHtjn3Oj5b9RjojQo8npoLeA/bNwkOkrSQ0wgrHzXk4tDRxGKJeg==", + "license": "Apache-2.0", + "dependencies": { + "@aws-crypto/supports-web-crypto": "^5.2.0", + "@aws-crypto/util": "^5.2.0", + "@aws-sdk/types": "^3.222.0", + "@aws-sdk/util-locate-window": "^3.0.0", + "@smithy/util-utf8": "^2.0.0", + "tslib": "^2.6.2" + } + }, + "node_modules/@aws-crypto/sha256-browser": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/@aws-crypto/sha256-browser/-/sha256-browser-5.2.0.tgz", + "integrity": "sha512-AXfN/lGotSQwu6HNcEsIASo7kWXZ5HYWvfOmSNKDsEqC4OashTp8alTmaz+F7TC2L083SFv5RdB+qU3Vs1kZqw==", + "license": "Apache-2.0", + "dependencies": { + "@aws-crypto/sha256-js": "^5.2.0", + "@aws-crypto/supports-web-crypto": "^5.2.0", + "@aws-crypto/util": "^5.2.0", + "@aws-sdk/types": "^3.222.0", + "@aws-sdk/util-locate-window": "^3.0.0", + "@smithy/util-utf8": "^2.0.0", + "tslib": "^2.6.2" + } + }, + "node_modules/@aws-crypto/sha256-js": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/@aws-crypto/sha256-js/-/sha256-js-5.2.0.tgz", + "integrity": "sha512-FFQQyu7edu4ufvIZ+OadFpHHOt+eSTBaYaki44c+akjg7qZg9oOQeLlk77F6tSYqjDAFClrHJk9tMf0HdVyOvA==", + "license": "Apache-2.0", + "dependencies": { + "@aws-crypto/util": "^5.2.0", + "@aws-sdk/types": "^3.222.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-crypto/supports-web-crypto": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/@aws-crypto/supports-web-crypto/-/supports-web-crypto-5.2.0.tgz", + "integrity": "sha512-iAvUotm021kM33eCdNfwIN//F77/IADDSs58i+MDaOqFrVjZo9bAal0NK7HurRuWLLpF1iLX7gbWrjHjeo+YFg==", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + } + }, + "node_modules/@aws-crypto/util": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/@aws-crypto/util/-/util-5.2.0.tgz", + "integrity": "sha512-4RkU9EsI6ZpBve5fseQlGNUWKMa1RLPQ1dnjnQoe07ldfIzcsGb5hC5W0Dm7u423KWzawlrpbjXBrXCEv9zazQ==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "^3.222.0", + "@smithy/util-utf8": "^2.0.0", + "tslib": "^2.6.2" + } + }, + "node_modules/@aws-sdk/checksums": { + "version": "3.1000.6", + "resolved": "https://registry.npmjs.org/@aws-sdk/checksums/-/checksums-3.1000.6.tgz", + "integrity": "sha512-RMCrCteiUwYTEv2G9zfP/BEuKHv57665vVieJyp9cf8VgilWxP/KrWVtMdfdDlIH8nFhvu3rIMc29z3ebGEZ1w==", + "license": "Apache-2.0", + "dependencies": { + "@aws-crypto/crc32": "5.2.0", + "@aws-crypto/crc32c": "5.2.0", + "@aws-crypto/util": "5.2.0", + "@aws-sdk/core": "^3.974.21", + "@aws-sdk/types": "^3.973.13", + "@smithy/core": "^3.24.6", + "@smithy/types": "^4.14.3", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@aws-sdk/client-s3": { + "version": "3.1070.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-s3/-/client-s3-3.1070.0.tgz", + "integrity": "sha512-B/OUiCqGQ4Zr7v9gFFyiuitKN2c0PIgvOlQb5bYg1SM2y0F8a5JQ7FNsjRcl+d2PqYWLHwHx12CvZDyLn4KxIw==", + "license": "Apache-2.0", + "dependencies": { + "@aws-crypto/sha1-browser": "5.2.0", + "@aws-crypto/sha256-browser": "5.2.0", + "@aws-crypto/sha256-js": "5.2.0", + "@aws-sdk/core": "^3.974.21", + "@aws-sdk/credential-provider-node": "^3.972.56", + "@aws-sdk/middleware-flexible-checksums": "^3.974.31", + "@aws-sdk/middleware-sdk-s3": "^3.972.52", + "@aws-sdk/signature-v4-multi-region": "^3.996.35", + "@aws-sdk/types": "^3.973.13", + "@smithy/core": "^3.24.6", + "@smithy/fetch-http-handler": "^5.4.6", + "@smithy/node-http-handler": "^4.7.6", + "@smithy/types": "^4.14.3", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@aws-sdk/core": { + "version": "3.974.21", + "resolved": "https://registry.npmjs.org/@aws-sdk/core/-/core-3.974.21.tgz", + "integrity": "sha512-P5JAHvn4dTi96UsAGS67LVOqqpUNNRhnfFXqzCYtdBIGZtqBue4CXvRr9YenOO7PALj/Pn8uuyw53FBCiCYw8w==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "^3.973.13", + "@aws-sdk/xml-builder": "^3.972.30", + "@aws/lambda-invoke-store": "^0.2.2", + "@smithy/core": "^3.24.6", + "@smithy/signature-v4": "^5.4.6", + "@smithy/types": "^4.14.3", + "bowser": "^2.11.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-env": { + "version": "3.972.47", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-env/-/credential-provider-env-3.972.47.tgz", + "integrity": "sha512-3YoPwJczcc+MtX2xxXaYaOOWO6xKUJr1ZIIDIFuninr51BYONVVcF/CP8K2xfVRC/PztJjqKWxNGFH7BWQAw1Q==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "^3.974.21", + "@aws-sdk/types": "^3.973.13", + "@smithy/core": "^3.24.6", + "@smithy/types": "^4.14.3", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-http": { + "version": "3.972.49", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-http/-/credential-provider-http-3.972.49.tgz", + "integrity": "sha512-2UtGUPy+x3lqyceHrtC1uEuVxBZbDalPF6KAFqBwYgm4edWdBrZKNnCqzDs7KynWUvEC6mrR+ojRk+ZgQz9C2w==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "^3.974.21", + "@aws-sdk/types": "^3.973.13", + "@smithy/core": "^3.24.6", + "@smithy/fetch-http-handler": "^5.4.6", + "@smithy/node-http-handler": "^4.7.6", + "@smithy/types": "^4.14.3", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-ini": { + "version": "3.972.54", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-ini/-/credential-provider-ini-3.972.54.tgz", + "integrity": "sha512-Hx4gO4YRjFwitf3MVl3cDwYe1aryJthC4txVl9b+JAURovA50M2ywf9r8j1E/Q6SCTPT4qQpjOAbKYIC9CG+Vw==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "^3.974.21", + "@aws-sdk/credential-provider-env": "^3.972.47", + "@aws-sdk/credential-provider-http": "^3.972.49", + "@aws-sdk/credential-provider-login": "^3.972.53", + "@aws-sdk/credential-provider-process": "^3.972.47", + "@aws-sdk/credential-provider-sso": "^3.972.53", + "@aws-sdk/credential-provider-web-identity": "^3.972.53", + "@aws-sdk/nested-clients": "^3.997.21", + "@aws-sdk/types": "^3.973.13", + "@smithy/core": "^3.24.6", + "@smithy/credential-provider-imds": "^4.3.7", + "@smithy/types": "^4.14.3", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-login": { + "version": "3.972.53", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-login/-/credential-provider-login-3.972.53.tgz", + "integrity": "sha512-+71sluhkgPqdhbbD3UDwUpj24GCkng9HQx6z7qoBFb8dwkF4ktpOcVKDeHpgg8PvBgLYwAnUYLTEGRC/PniCiQ==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "^3.974.21", + "@aws-sdk/nested-clients": "^3.997.21", + "@aws-sdk/types": "^3.973.13", + "@smithy/core": "^3.24.6", + "@smithy/types": "^4.14.3", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-node": { + "version": "3.972.56", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-node/-/credential-provider-node-3.972.56.tgz", + "integrity": "sha512-iI+4o0dvQQ4NHel4FMDiFy5q2gaU/ryLK3niOsoPccAt9WLFRkV4XTYPWRr9XvmBUqEzXG73S4p/8gm0Lu/W3A==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/credential-provider-env": "^3.972.47", + "@aws-sdk/credential-provider-http": "^3.972.49", + "@aws-sdk/credential-provider-ini": "^3.972.54", + "@aws-sdk/credential-provider-process": "^3.972.47", + "@aws-sdk/credential-provider-sso": "^3.972.53", + "@aws-sdk/credential-provider-web-identity": "^3.972.53", + "@aws-sdk/types": "^3.973.13", + "@smithy/core": "^3.24.6", + "@smithy/credential-provider-imds": "^4.3.7", + "@smithy/types": "^4.14.3", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-process": { + "version": "3.972.47", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-process/-/credential-provider-process-3.972.47.tgz", + "integrity": "sha512-tAizPm9IFo/PHn06c+LQJlzfY2AGOlyF0CUljFejrU6LcZBjnk8pmbZK3/xoIDdnIzjEdbClfvY3mXfr818ZEg==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "^3.974.21", + "@aws-sdk/types": "^3.973.13", + "@smithy/core": "^3.24.6", + "@smithy/types": "^4.14.3", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-sso": { + "version": "3.972.53", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-sso/-/credential-provider-sso-3.972.53.tgz", + "integrity": "sha512-pUXE3fu4tfEDV8BksIgf4dXvuIH10FhwHMl/wu8rBD5T1sMpryQWFVitH3kdPS90wlgrGYJQ/meQTSPacyZfeg==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "^3.974.21", + "@aws-sdk/nested-clients": "^3.997.21", + "@aws-sdk/token-providers": "3.1069.0", + "@aws-sdk/types": "^3.973.13", + "@smithy/core": "^3.24.6", + "@smithy/types": "^4.14.3", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-web-identity": { + "version": "3.972.53", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-web-identity/-/credential-provider-web-identity-3.972.53.tgz", + "integrity": "sha512-JmMGlhVvSj8uSG9CpeDkJAXT35H89tc6v84iMgEIE75q4yp1MKVVKvopv6Gg28HJIR7hMNkojRF8H2m5W44wyg==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "^3.974.21", + "@aws-sdk/nested-clients": "^3.997.21", + "@aws-sdk/types": "^3.973.13", + "@smithy/core": "^3.24.6", + "@smithy/types": "^4.14.3", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@aws-sdk/middleware-flexible-checksums": { + "version": "3.974.31", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-flexible-checksums/-/middleware-flexible-checksums-3.974.31.tgz", + "integrity": "sha512-Yzj6NRYVZdBaCp7o1BwHGyeDBfixdeToLIAMprshIITEdl9wKVSiidVOfeaiH8FyeC1hBmBfDZFvs/aH1Y3xpw==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/checksums": "^3.1000.6", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@aws-sdk/middleware-sdk-s3": { + "version": "3.972.52", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-sdk-s3/-/middleware-sdk-s3-3.972.52.tgz", + "integrity": "sha512-rerjP08onRqkBh0AcCqip6GkKvESapmLoTgi1xysZ4C6a1xMrIMtTBcEbUb6EY71oeajnigeUD4KwZjtIO+aWQ==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "^3.974.21", + "@aws-sdk/signature-v4-multi-region": "^3.996.35", + "@aws-sdk/types": "^3.973.13", + "@smithy/core": "^3.24.6", + "@smithy/types": "^4.14.3", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@aws-sdk/nested-clients": { + "version": "3.997.21", + "resolved": "https://registry.npmjs.org/@aws-sdk/nested-clients/-/nested-clients-3.997.21.tgz", + "integrity": "sha512-eC7Vl7Qom/BGhZjG9GEqPwdQ/fk45hg1t5LP4EUxG5d1fdshLbaxCiwh/tszUzDX/4mW40mu2QsbeJJRPBbqUw==", + "license": "Apache-2.0", + "dependencies": { + "@aws-crypto/sha256-browser": "5.2.0", + "@aws-crypto/sha256-js": "5.2.0", + "@aws-sdk/core": "^3.974.21", + "@aws-sdk/signature-v4-multi-region": "^3.996.35", + "@aws-sdk/types": "^3.973.13", + "@smithy/core": "^3.24.6", + "@smithy/fetch-http-handler": "^5.4.6", + "@smithy/node-http-handler": "^4.7.6", + "@smithy/types": "^4.14.3", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@aws-sdk/s3-request-presigner": { + "version": "3.1070.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/s3-request-presigner/-/s3-request-presigner-3.1070.0.tgz", + "integrity": "sha512-T2YcuFOPTO7DzVO5/zNuVQWOOC+Fbv39RjzgiIRp3N45K86XMHBsmP3J8T/doZbkAnErjVY3wgQPXAduy5WTFQ==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "^3.974.21", + "@aws-sdk/signature-v4-multi-region": "^3.996.35", + "@aws-sdk/types": "^3.973.13", + "@smithy/core": "^3.24.6", + "@smithy/types": "^4.14.3", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@aws-sdk/signature-v4-multi-region": { + "version": "3.996.35", + "resolved": "https://registry.npmjs.org/@aws-sdk/signature-v4-multi-region/-/signature-v4-multi-region-3.996.35.tgz", + "integrity": "sha512-6L/VWs+Wch2stHemCGTmUNqKLMzURxQDK5boNG3Jn3kAOp71meDUuS5sbObpEvFxHDq0uWeSLFDNSYsjNt+Dlg==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "^3.973.13", + "@smithy/signature-v4": "^5.4.6", + "@smithy/types": "^4.14.3", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@aws-sdk/token-providers": { + "version": "3.1069.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/token-providers/-/token-providers-3.1069.0.tgz", + "integrity": "sha512-ks4X+kngC3PA5howV7Qu1TgG4bfC4jPykKdvw3nmBSXR9yZxRJouBholFSNQ5kY3L+Fgwyw+LCjzQmNi+KR91g==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "^3.974.21", + "@aws-sdk/nested-clients": "^3.997.21", + "@aws-sdk/types": "^3.973.13", + "@smithy/core": "^3.24.6", + "@smithy/types": "^4.14.3", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@aws-sdk/types": { + "version": "3.973.13", + "resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.973.13.tgz", + "integrity": "sha512-pEHZqRkAlHfnfAU9tK+WpKv/gBNjGJrHMgA3A0iYRGyswBS2t0pfez+lWlwktb3Bqa0ovh7w/QJTFwp3fDxLNg==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^4.14.3", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@aws-sdk/util-locate-window": { + "version": "3.965.8", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-locate-window/-/util-locate-window-3.965.8.tgz", + "integrity": "sha512-uUbMs1cBZPafD0ohUj6EwNf0fPZ534NvBxHox4hjX+0Rxq5paSYUem7+hi833pYrzrcnBATKIYpR02MDXT5M9g==", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@aws-sdk/xml-builder": { + "version": "3.972.30", + "resolved": "https://registry.npmjs.org/@aws-sdk/xml-builder/-/xml-builder-3.972.30.tgz", + "integrity": "sha512-StElZPEoBquWwNqw1AcfpzEyZqJvFxouG+mpDNYlcH6ZOrqd2CuIryv+8LV8gNHZUOyKyJF3Dq9vxaXEmDR9TQ==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^4.14.3", + "fast-xml-parser": "5.7.3", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@aws/lambda-invoke-store": { + "version": "0.2.4", + "resolved": "https://registry.npmjs.org/@aws/lambda-invoke-store/-/lambda-invoke-store-0.2.4.tgz", + "integrity": "sha512-iY8yvjE0y651BixKNPgmv1WrQc+GZ142sb0z4gYnChDDY2YqI4P/jsSopBWrKfAt7LOJAkOXt7rC/hms+WclQQ==", + "license": "Apache-2.0", + "engines": { + "node": ">=18.0.0" + } + }, "node_modules/@babel/code-frame": { "version": "7.29.0", "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.29.0.tgz", @@ -2556,6 +3005,18 @@ "url": "https://paulmillr.com/funding/" } }, + "node_modules/@nodable/entities": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@nodable/entities/-/entities-2.2.0.tgz", + "integrity": "sha512-9uGyhaQavEUMC8AIddIjau4NsnsXhou+j5sBAGojCM1oxmQpVKTWR/9JxABD6UAv12vpIms55fPZKFQEhG6uBg==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/nodable" + } + ], + "license": "MIT" + }, "node_modules/@nuxt/opencollective": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/@nuxt/opencollective/-/opencollective-0.4.1.tgz", @@ -2708,6 +3169,126 @@ "@sinonjs/commons": "^3.0.1" } }, + "node_modules/@smithy/core": { + "version": "3.25.0", + "resolved": "https://registry.npmjs.org/@smithy/core/-/core-3.25.0.tgz", + "integrity": "sha512-TTD6el7tvKyafkXBf7XO3jLOE+qVxOTrLjp/fEGiV3BMfUHK/LfdYlQO9YgZvzxC7kqA3H/IhJXNqQgnbgjb7A==", + "license": "Apache-2.0", + "dependencies": { + "@aws-crypto/crc32": "5.2.0", + "@smithy/types": "^4.15.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/credential-provider-imds": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/@smithy/credential-provider-imds/-/credential-provider-imds-4.4.0.tgz", + "integrity": "sha512-pPQmNdEvMJttv9z2kdYxoui83p/nr32zjMf0aMfmzmGmFEgKXUfy0vXiNg0fx4R5XLQzmJBLM9Wg0guEq2/q8A==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/core": "^3.25.0", + "@smithy/types": "^4.15.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/fetch-http-handler": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/@smithy/fetch-http-handler/-/fetch-http-handler-5.5.0.tgz", + "integrity": "sha512-OG8kBYAgX7lf32+xLzgirvuLffn1KNoszaSiButt45i2cRa5irk8LQXLYQ5Smij1SBTN4KMNcBsRwRrLPfIGyA==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/core": "^3.25.0", + "@smithy/types": "^4.15.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/is-array-buffer": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@smithy/is-array-buffer/-/is-array-buffer-2.2.0.tgz", + "integrity": "sha512-GGP3O9QFD24uGeAXYUjwSTXARoqpZykHadOmA8G5vfJPK0/DC67qa//0qvqrJzL1xc8WQWX7/yc7fwudjPHPhA==", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@smithy/node-http-handler": { + "version": "4.8.0", + "resolved": "https://registry.npmjs.org/@smithy/node-http-handler/-/node-http-handler-4.8.0.tgz", + "integrity": "sha512-Mq7TNt/VhlEWiYRLQGpzUWeUxh899UGpjKh7Ru0WVIDIjnE+cTRAn0NYlFQ6bWfsQnKnpCbWJj86HzmcG0qEdg==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/core": "^3.25.0", + "@smithy/types": "^4.15.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/signature-v4": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/@smithy/signature-v4/-/signature-v4-5.5.0.tgz", + "integrity": "sha512-vW6UdK7e7gV2wU/tXRsPq4pMQMusb8VymdVOyIFNA1FtyRmEClRFkYDtYI8UcO/HM0wK3qqjvvQs3HOlbgMbdg==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/core": "^3.25.0", + "@smithy/types": "^4.15.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/types": { + "version": "4.15.0", + "resolved": "https://registry.npmjs.org/@smithy/types/-/types-4.15.0.tgz", + "integrity": "sha512-Z5TAOxygoFvybJV3igo5SloFflSokHx2hu1eFA+DxDTcn+FtKxUSui+rbTRG1pAafMA888Z3MVvCWUuvCrTXjg==", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/util-buffer-from": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@smithy/util-buffer-from/-/util-buffer-from-2.2.0.tgz", + "integrity": "sha512-IJdWBbTcMQ6DA0gdNhh/BwrLkDR+ADW5Kr1aZmd4k3DIF6ezMV4R2NIAmT08wQJ3yUK82thHWmC/TnK/wpMMIA==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/is-array-buffer": "^2.2.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@smithy/util-utf8": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/@smithy/util-utf8/-/util-utf8-2.3.0.tgz", + "integrity": "sha512-R8Rdn8Hy72KKcebgLiv8jQcQkXoLMOGGv5uI1/k0l+snqkOzQ1R0ChUBCxWMlBsFMekWjq0wRudIweFs7sKT5A==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/util-buffer-from": "^2.2.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=14.0.0" + } + }, "node_modules/@tokenizer/inflate": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/@tokenizer/inflate/-/inflate-0.4.1.tgz", @@ -4121,6 +4702,18 @@ "url": "https://github.com/sponsors/jonschlinkert" } }, + "node_modules/anynum": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/anynum/-/anynum-1.0.0.tgz", + "integrity": "sha512-xjR9/zBVnUOP6ztMIIgShjsxui80nQUQH+5xJnvrYLs+90bF25/KJqaAi8mk+B4RDtX1Nspi6fmp4YTEts8SfA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/NaturalIntelligence" + } + ], + "license": "MIT" + }, "node_modules/append-field": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/append-field/-/append-field-1.0.0.tgz", @@ -4362,6 +4955,12 @@ "url": "https://opencollective.com/express" } }, + "node_modules/bowser": { + "version": "2.14.1", + "resolved": "https://registry.npmjs.org/bowser/-/bowser-2.14.1.tgz", + "integrity": "sha512-tzPjzCxygAKWFOJP011oxFHs57HzIhOEracIgAePE4pqB3LikALKnSzUyU4MGs9/iCEUuHlAJTjTc5M+u7YEGg==", + "license": "MIT" + }, "node_modules/brace-expansion": { "version": "1.1.14", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.14.tgz", @@ -5675,6 +6274,43 @@ ], "license": "BSD-3-Clause" }, + "node_modules/fast-xml-builder": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/fast-xml-builder/-/fast-xml-builder-1.2.0.tgz", + "integrity": "sha512-00aAWieqff+ZJhsXA4g1g7M8k+7AYoMUUHF+/zFb5U6Uv/P0Vl4QZo84/IcufzYalLuEj9928bXN9PbbFzMF0Q==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/NaturalIntelligence" + } + ], + "license": "MIT", + "dependencies": { + "path-expression-matcher": "^1.5.0", + "xml-naming": "^0.1.0" + } + }, + "node_modules/fast-xml-parser": { + "version": "5.7.3", + "resolved": "https://registry.npmjs.org/fast-xml-parser/-/fast-xml-parser-5.7.3.tgz", + "integrity": "sha512-C0AaNuC+mscy6vrAQKAc/rMq+zAPHodfHGZu4sGVehvAQt/JLG1O5zEcYcXSY5zSqr4YVgxsB+pHXTq0i7eDlg==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/NaturalIntelligence" + } + ], + "license": "MIT", + "dependencies": { + "@nodable/entities": "^2.1.0", + "fast-xml-builder": "^1.1.7", + "path-expression-matcher": "^1.5.0", + "strnum": "^2.2.3" + }, + "bin": { + "fxparser": "src/cli/cli.js" + } + }, "node_modules/fb-watchman": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/fb-watchman/-/fb-watchman-2.0.2.tgz", @@ -8361,6 +8997,21 @@ "node": ">=8" } }, + "node_modules/path-expression-matcher": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/path-expression-matcher/-/path-expression-matcher-1.5.0.tgz", + "integrity": "sha512-cbrerZV+6rvdQrrD+iGMcZFEiiSrbv9Tfdkvnusy6y0x0GKBXREFg/Y65GhIfm0tnLntThhzCnfKwp1WRjeCyQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/NaturalIntelligence" + } + ], + "license": "MIT", + "engines": { + "node": ">=14.0.0" + } + }, "node_modules/path-is-absolute": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", @@ -9285,6 +9936,21 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/strnum": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/strnum/-/strnum-2.4.0.tgz", + "integrity": "sha512-sHrVyWWdq28RbhjuJdZsA1SnGRJV6NiXbk6AXBxDOsgAcA+lmpUZCYjOdLBxkXMwis6RRe7dlZt4VlIWFVzkmg==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/NaturalIntelligence" + } + ], + "license": "MIT", + "dependencies": { + "anynum": "^1.0.0" + } + }, "node_modules/strtok3": { "version": "10.3.5", "resolved": "https://registry.npmjs.org/strtok3/-/strtok3-10.3.5.tgz", @@ -10502,6 +11168,21 @@ "node": "^14.17.0 || ^16.13.0 || >=18.0.0" } }, + "node_modules/xml-naming": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/xml-naming/-/xml-naming-0.1.0.tgz", + "integrity": "sha512-k8KO9hrMyNk6tUWqUfkTEZbezRRpONVOzUTnc97VnCvyj6Tf9lyUR9EDAIeiVLv56jsMcoXEwjW8Kv5yPY52lw==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/NaturalIntelligence" + } + ], + "license": "MIT", + "engines": { + "node": ">=16.0.0" + } + }, "node_modules/y18n": { "version": "5.0.8", "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", diff --git a/backend/nest-api/package.json b/backend/nest-api/package.json index 951573a..5de6e04 100644 --- a/backend/nest-api/package.json +++ b/backend/nest-api/package.json @@ -20,6 +20,8 @@ "test:e2e": "jest --config ./test/jest-e2e.json" }, "dependencies": { + "@aws-sdk/client-s3": "^3.1070.0", + "@aws-sdk/s3-request-presigner": "^3.1070.0", "@nestjs/axios": "^4.0.1", "@nestjs/common": "^11.0.1", "@nestjs/core": "^11.0.1", diff --git a/backend/nest-api/src/license/license.controller.ts b/backend/nest-api/src/license/license.controller.ts index 8ef184a..79b8fd0 100644 --- a/backend/nest-api/src/license/license.controller.ts +++ b/backend/nest-api/src/license/license.controller.ts @@ -152,6 +152,14 @@ export class LicenseController { }); } + @Get('get-upload-url') + async getUploadUrl( + @Query('fileName') fileName: string, + @Query('fileType') fileType: string, + ) { + return this.licenseService.getS3UploadUrl(fileName, fileType); + } + @ApiOperation({ summary: 'Get current user active license' }) @Get('my-license') async getMyLicense(@Request() req: AuthRequest) { diff --git a/backend/nest-api/src/license/license.service.ts b/backend/nest-api/src/license/license.service.ts index d494ddf..a4b421c 100644 --- a/backend/nest-api/src/license/license.service.ts +++ b/backend/nest-api/src/license/license.service.ts @@ -6,6 +6,8 @@ import { } from '@nestjs/common'; import { PrismaService } from '../prisma/prisma.service'; import * as crypto from 'crypto'; +import { S3Client, PutObjectCommand } from '@aws-sdk/client-s3'; +import { getSignedUrl } from '@aws-sdk/s3-request-presigner'; export interface VehicleCategoryData { vehicleClass: string; @@ -39,7 +41,39 @@ export interface UpdateLicenseData { @Injectable() export class LicenseService { - constructor(private prisma: PrismaService) {} + private s3Client: S3Client; + + constructor(private prisma: PrismaService) { + this.s3Client = new S3Client({ + region: process.env.AWS_S3_REGION, + credentials: { + accessKeyId: process.env.AWS_ACCESS_KEY_ID, + secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY, + }, + }); + } + + async getS3UploadUrl(fileName: string, fileType: string) { + const bucketName = process.env.AWS_S3_BUCKET_NAME; + const cleanFileName = fileName.replace(/\s+/g, '-'); + const uniqueFileName = `licenses/${Date.now()}-${cleanFileName}`; + + const command = new PutObjectCommand({ + Bucket: bucketName, + Key: uniqueFileName, + ContentType: fileType, + }); + + const uploadUrl = await getSignedUrl(this.s3Client, command, { + expiresIn: 60, + }); + const publicFileUrl = `https://${bucketName}.s3.${process.env.AWS_S3_REGION}.amazonaws.com/${uniqueFileName}`; + + return { + uploadUrl, + fileUrl: publicFileUrl, + }; + } async createLicense(data: CreateLicenseData) { let user = await this.prisma.user.findUnique({ @@ -52,7 +86,7 @@ export class LicenseService { nic_No: data.nicNo, name: 'Pending App Registration', password: 'NOT_REGISTERED', - mobile_Phone_No: 'PENDING', + mobile_Phone_No: `PENDING_${data.nicNo}`, device_Id: 'PENDING', }, }); From 48af49c3e525321955c90ad427fb64ac928cffbc Mon Sep 17 00:00:00 2001 From: venushathi <148311558+vthish@users.noreply.github.com> Date: Wed, 17 Jun 2026 22:03:02 +0530 Subject: [PATCH 02/12] Update main.ts (Add dotenv pkg) --- backend/nest-api/package-lock.json | 13 +++++++++++++ backend/nest-api/package.json | 1 + backend/nest-api/src/main.ts | 3 +++ 3 files changed, 17 insertions(+) diff --git a/backend/nest-api/package-lock.json b/backend/nest-api/package-lock.json index a4ed7b9..18ef66d 100644 --- a/backend/nest-api/package-lock.json +++ b/backend/nest-api/package-lock.json @@ -23,6 +23,7 @@ "bcrypt": "^6.0.0", "class-transformer": "^0.5.1", "class-validator": "^0.15.1", + "dotenv": "^17.4.2", "passport": "^0.7.0", "passport-jwt": "^4.0.1", "reflect-metadata": "^0.2.2", @@ -5710,6 +5711,18 @@ "node": ">=0.3.1" } }, + "node_modules/dotenv": { + "version": "17.4.2", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-17.4.2.tgz", + "integrity": "sha512-nI4U3TottKAcAD9LLud4Cb7b2QztQMUEfHbvhTH09bqXTxnSie8WnjPALV/WMCrJZ6UV/qHJ6L03OqO3LcdYZw==", + "license": "BSD-2-Clause", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://dotenvx.com" + } + }, "node_modules/dunder-proto": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", diff --git a/backend/nest-api/package.json b/backend/nest-api/package.json index 5de6e04..f3bfc21 100644 --- a/backend/nest-api/package.json +++ b/backend/nest-api/package.json @@ -34,6 +34,7 @@ "bcrypt": "^6.0.0", "class-transformer": "^0.5.1", "class-validator": "^0.15.1", + "dotenv": "^17.4.2", "passport": "^0.7.0", "passport-jwt": "^4.0.1", "reflect-metadata": "^0.2.2", diff --git a/backend/nest-api/src/main.ts b/backend/nest-api/src/main.ts index 93c79d0..e12e258 100644 --- a/backend/nest-api/src/main.ts +++ b/backend/nest-api/src/main.ts @@ -1,6 +1,9 @@ import { NestFactory } from '@nestjs/core'; import { AppModule } from './app.module'; import { DocumentBuilder, SwaggerModule } from '@nestjs/swagger'; +import * as dotenv from 'dotenv'; + +dotenv.config(); async function bootstrap() { const app = await NestFactory.create(AppModule); From 40e8d7cc5073e72330a894b2dc6bc8317fe28869 Mon Sep 17 00:00:00 2001 From: venushathi <148311558+vthish@users.noreply.github.com> Date: Wed, 17 Jun 2026 22:20:08 +0530 Subject: [PATCH 03/12] Add ConfigModule to load AWS env vars --- backend/nest-api/package-lock.json | 55 ++++++++++++++++++++++++++++++ backend/nest-api/package.json | 1 + backend/nest-api/src/app.module.ts | 4 +++ 3 files changed, 60 insertions(+) diff --git a/backend/nest-api/package-lock.json b/backend/nest-api/package-lock.json index 18ef66d..eee104c 100644 --- a/backend/nest-api/package-lock.json +++ b/backend/nest-api/package-lock.json @@ -13,6 +13,7 @@ "@aws-sdk/s3-request-presigner": "^3.1070.0", "@nestjs/axios": "^4.0.1", "@nestjs/common": "^11.0.1", + "@nestjs/config": "^4.0.4", "@nestjs/core": "^11.0.1", "@nestjs/jwt": "^11.0.2", "@nestjs/passport": "^11.0.5", @@ -2804,6 +2805,33 @@ } } }, + "node_modules/@nestjs/config": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/@nestjs/config/-/config-4.0.4.tgz", + "integrity": "sha512-CJPjNitr0bAufSEnRe2N+JbnVmMmDoo6hvKCPzXgZoGwJSmp/dZPk9f/RMbuD/+Q1ZJPjwsRpq0vxna++Knwow==", + "license": "MIT", + "dependencies": { + "dotenv": "17.4.1", + "dotenv-expand": "12.0.3", + "lodash": "4.18.1" + }, + "peerDependencies": { + "@nestjs/common": "^10.0.0 || ^11.0.0", + "rxjs": "^7.1.0" + } + }, + "node_modules/@nestjs/config/node_modules/dotenv": { + "version": "17.4.1", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-17.4.1.tgz", + "integrity": "sha512-k8DaKGP6r1G30Lx8V4+pCsLzKr8vLmV2paqEj1Y55GdAgJuIqpRp5FfajGF8KtwMxCz9qJc6wUIJnm053d/WCw==", + "license": "BSD-2-Clause", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://dotenvx.com" + } + }, "node_modules/@nestjs/core": { "version": "11.1.23", "resolved": "https://registry.npmjs.org/@nestjs/core/-/core-11.1.23.tgz", @@ -5723,6 +5751,33 @@ "url": "https://dotenvx.com" } }, + "node_modules/dotenv-expand": { + "version": "12.0.3", + "resolved": "https://registry.npmjs.org/dotenv-expand/-/dotenv-expand-12.0.3.tgz", + "integrity": "sha512-uc47g4b+4k/M/SeaW1y4OApx+mtLWl92l5LMPP0GNXctZqELk+YGgOPIIC5elYmUH4OuoK3JLhuRUYegeySiFA==", + "license": "BSD-2-Clause", + "dependencies": { + "dotenv": "^16.4.5" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://dotenvx.com" + } + }, + "node_modules/dotenv-expand/node_modules/dotenv": { + "version": "16.6.1", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.6.1.tgz", + "integrity": "sha512-uBq4egWHTcTt33a72vpSG0z3HnPuIl6NqYcTrKEg2azoEyl2hpW0zqlxysq2pK9HlDIHyHyakeYaYnSAwd8bow==", + "license": "BSD-2-Clause", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://dotenvx.com" + } + }, "node_modules/dunder-proto": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", diff --git a/backend/nest-api/package.json b/backend/nest-api/package.json index f3bfc21..0929760 100644 --- a/backend/nest-api/package.json +++ b/backend/nest-api/package.json @@ -24,6 +24,7 @@ "@aws-sdk/s3-request-presigner": "^3.1070.0", "@nestjs/axios": "^4.0.1", "@nestjs/common": "^11.0.1", + "@nestjs/config": "^4.0.4", "@nestjs/core": "^11.0.1", "@nestjs/jwt": "^11.0.2", "@nestjs/passport": "^11.0.5", diff --git a/backend/nest-api/src/app.module.ts b/backend/nest-api/src/app.module.ts index e4104a3..c349575 100644 --- a/backend/nest-api/src/app.module.ts +++ b/backend/nest-api/src/app.module.ts @@ -1,4 +1,5 @@ import { Module } from '@nestjs/common'; +import { ConfigModule } from '@nestjs/config'; import { AppController } from './app.controller'; import { AppService } from './app.service'; import { PrismaModule } from './prisma/prisma.module'; @@ -10,6 +11,9 @@ import { AuthModule } from './auth/auth.module'; @Module({ imports: [ + ConfigModule.forRoot({ + isGlobal: true, + }), PrismaModule, UsersModule, LicenseModule, From 1bbce0947f448ec61621cd75d8b0122efaed9293 Mon Sep 17 00:00:00 2001 From: venushathi <148311558+vthish@users.noreply.github.com> Date: Thu, 18 Jun 2026 08:14:33 +0530 Subject: [PATCH 04/12] Change the variable names --- backend/nest-api/src/license/license.service.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/backend/nest-api/src/license/license.service.ts b/backend/nest-api/src/license/license.service.ts index a4b421c..521b9d1 100644 --- a/backend/nest-api/src/license/license.service.ts +++ b/backend/nest-api/src/license/license.service.ts @@ -45,7 +45,7 @@ export class LicenseService { constructor(private prisma: PrismaService) { this.s3Client = new S3Client({ - region: process.env.AWS_S3_REGION, + region: process.env.AWS_REGION, credentials: { accessKeyId: process.env.AWS_ACCESS_KEY_ID, secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY, @@ -67,7 +67,7 @@ export class LicenseService { const uploadUrl = await getSignedUrl(this.s3Client, command, { expiresIn: 60, }); - const publicFileUrl = `https://${bucketName}.s3.${process.env.AWS_S3_REGION}.amazonaws.com/${uniqueFileName}`; + const publicFileUrl = `https://${bucketName}.s3.${process.env.AWS_REGION}.amazonaws.com/${uniqueFileName}`; return { uploadUrl, From a7d2cc8f474c7e08b71f17b0959d5db85f3b2efc Mon Sep 17 00:00:00 2001 From: venushathi <148311558+vthish@users.noreply.github.com> Date: Thu, 18 Jun 2026 08:58:44 +0530 Subject: [PATCH 05/12] Update the license.serivice.ts and add thie env for docker -compose.yaml --- .../nest-api/src/license/license.service.ts | 28 +++++++++++++++---- docker-compose.yml | 2 ++ 2 files changed, 24 insertions(+), 6 deletions(-) diff --git a/backend/nest-api/src/license/license.service.ts b/backend/nest-api/src/license/license.service.ts index 521b9d1..403e032 100644 --- a/backend/nest-api/src/license/license.service.ts +++ b/backend/nest-api/src/license/license.service.ts @@ -5,6 +5,7 @@ import { UnauthorizedException, } from '@nestjs/common'; import { PrismaService } from '../prisma/prisma.service'; +import { ConfigService } from '@nestjs/config'; import * as crypto from 'crypto'; import { S3Client, PutObjectCommand } from '@aws-sdk/client-s3'; import { getSignedUrl } from '@aws-sdk/s3-request-presigner'; @@ -43,18 +44,33 @@ export interface UpdateLicenseData { export class LicenseService { private s3Client: S3Client; - constructor(private prisma: PrismaService) { + constructor( + private prisma: PrismaService, + private configService: ConfigService, + ) { + const region = + this.configService.get('AWS_REGION') || 'ap-southeast-1'; + const accessKeyId = + this.configService.get('AWS_ACCESS_KEY_ID') || ''; + const secretAccessKey = + this.configService.get('AWS_SECRET_ACCESS_KEY') || ''; + this.s3Client = new S3Client({ - region: process.env.AWS_REGION, + region: region, credentials: { - accessKeyId: process.env.AWS_ACCESS_KEY_ID, - secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY, + accessKeyId: accessKeyId, + secretAccessKey: secretAccessKey, }, }); } async getS3UploadUrl(fileName: string, fileType: string) { - const bucketName = process.env.AWS_S3_BUCKET_NAME; + const bucketName = + this.configService.get('AWS_S3_BUCKET_NAME') || + 'auto-ledger-images'; + const region = + this.configService.get('AWS_REGION') || 'ap-southeast-1'; + const cleanFileName = fileName.replace(/\s+/g, '-'); const uniqueFileName = `licenses/${Date.now()}-${cleanFileName}`; @@ -67,7 +83,7 @@ export class LicenseService { const uploadUrl = await getSignedUrl(this.s3Client, command, { expiresIn: 60, }); - const publicFileUrl = `https://${bucketName}.s3.${process.env.AWS_REGION}.amazonaws.com/${uniqueFileName}`; + const publicFileUrl = `https://${bucketName}.s3.${region}.amazonaws.com/${uniqueFileName}`; return { uploadUrl, diff --git a/docker-compose.yml b/docker-compose.yml index cd1a570..9d87443 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -30,6 +30,8 @@ services: restart: always ports: - "3000:3000" + env_file: + - ./backend/nest-api/.env environment: - DATABASE_URL=postgresql://admin:password123@db:5432/auto_ledger_db?schema=public - JWT_SECRET=AutoLedgerSecretKey2026 From 544268a04cce98fe68e218b51ae5aa2b3e670698 Mon Sep 17 00:00:00 2001 From: venushathi <148311558+vthish@users.noreply.github.com> Date: Thu, 18 Jun 2026 09:17:56 +0530 Subject: [PATCH 06/12] Fix undefined region in S3 public URL --- backend/nest-api/src/license/license.service.ts | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/backend/nest-api/src/license/license.service.ts b/backend/nest-api/src/license/license.service.ts index 403e032..c533860 100644 --- a/backend/nest-api/src/license/license.service.ts +++ b/backend/nest-api/src/license/license.service.ts @@ -68,8 +68,7 @@ export class LicenseService { const bucketName = this.configService.get('AWS_S3_BUCKET_NAME') || 'auto-ledger-images'; - const region = - this.configService.get('AWS_REGION') || 'ap-southeast-1'; + const region = process.env.AWS_REGION || 'ap-southeast-1'; const cleanFileName = fileName.replace(/\s+/g, '-'); const uniqueFileName = `licenses/${Date.now()}-${cleanFileName}`; @@ -84,7 +83,6 @@ export class LicenseService { expiresIn: 60, }); const publicFileUrl = `https://${bucketName}.s3.${region}.amazonaws.com/${uniqueFileName}`; - return { uploadUrl, fileUrl: publicFileUrl, From 07b1cc7f872872f390f2e84660044a08936219c2 Mon Sep 17 00:00:00 2001 From: venushathi <148311558+vthish@users.noreply.github.com> Date: Thu, 18 Jun 2026 09:48:36 +0530 Subject: [PATCH 07/12] Add build command to Dockerfile --- backend/nest-api/Dockerfile | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/backend/nest-api/Dockerfile b/backend/nest-api/Dockerfile index 835d9a9..a5bb526 100644 --- a/backend/nest-api/Dockerfile +++ b/backend/nest-api/Dockerfile @@ -1,23 +1,17 @@ -# Base image FROM node:20-alpine -# Create app directory WORKDIR /usr/src/app -# Copy dependency definitions COPY package*.json ./ -# Install dependencies (only production for smaller size, but let's keep all for now to be safe) RUN npm install -# Copy all files (including the dist folder we will build locally) COPY . . -# Generate Prisma Client RUN npx prisma generate -# Expose port +RUN npm run build + EXPOSE 3000 -# Start the application using the local dist folder CMD [ "npm", "run", "start:prod" ] \ No newline at end of file From 466b959df4fcb42f0d34f9738ccf23d8f5b2e3e6 Mon Sep 17 00:00:00 2001 From: Venusha Thishan <148311558+vthish@users.noreply.github.com> Date: Thu, 18 Jun 2026 10:08:12 +0530 Subject: [PATCH 08/12] Update Dockerfile --- backend/nest-api/Dockerfile | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/backend/nest-api/Dockerfile b/backend/nest-api/Dockerfile index a5bb526..55a399e 100644 --- a/backend/nest-api/Dockerfile +++ b/backend/nest-api/Dockerfile @@ -1,4 +1,6 @@ -FROM node:20-alpine +FROM node:20-slim + +RUN apt-get update -y && apt-get install -y openssl WORKDIR /usr/src/app @@ -14,4 +16,4 @@ RUN npm run build EXPOSE 3000 -CMD [ "npm", "run", "start:prod" ] \ No newline at end of file +CMD [ "npm", "run", "start" ] From 843c0ef6d77fb09326ee9bad2ea63ed4bdc74028 Mon Sep 17 00:00:00 2001 From: venushathi <148311558+vthish@users.noreply.github.com> Date: Thu, 18 Jun 2026 19:56:38 +0530 Subject: [PATCH 09/12] Upadate officers controller and service files adding new get endpoints and logics --- .../src/officers/officers.controller.ts | 12 ++++++ .../nest-api/src/officers/officers.service.ts | 38 ++++++++++++++++++- 2 files changed, 48 insertions(+), 2 deletions(-) diff --git a/backend/nest-api/src/officers/officers.controller.ts b/backend/nest-api/src/officers/officers.controller.ts index ea76db8..f0b0a10 100644 --- a/backend/nest-api/src/officers/officers.controller.ts +++ b/backend/nest-api/src/officers/officers.controller.ts @@ -145,4 +145,16 @@ export class OfficersController { async getOfficerShifts(@Param('id') id: string) { return this.officersService.getOfficerShifts(id); } + + @Roles('POLICE_ADMIN') + @Get('divisions') + async getAllDivisions() { + return this.officersService.getAllDivisions(); + } + + @Roles('POLICE_ADMIN') + @Get('divisional-heads') + async getAllDivisionalHeads() { + return this.officersService.getAllDivisionalHeads(); + } } diff --git a/backend/nest-api/src/officers/officers.service.ts b/backend/nest-api/src/officers/officers.service.ts index f54d56c..b168ba2 100644 --- a/backend/nest-api/src/officers/officers.service.ts +++ b/backend/nest-api/src/officers/officers.service.ts @@ -43,26 +43,37 @@ export class OfficersService { name: string; passwordStr: string; }) { - const existingDivision = await this.prisma.division.findUnique({ - where: { division_Name: data.divisionName }, + const existingDivision = await this.prisma.division.findFirst({ + where: { + OR: [ + { division_Name: data.divisionName }, + { division_Id: data.divisionName }, + ], + }, include: { divisionalHead: true }, }); + if (!existingDivision) throw new NotFoundException('Division not found'); + if (existingDivision.divisionalHead) throw new BadRequestException( 'This Division already has a Head assigned', ); + const existingUsername = await this.prisma.divisional_Head.findUnique({ where: { username: data.username }, }); if (existingUsername) throw new BadRequestException('Head username already exists'); + const existingEmail = await this.prisma.divisional_Head.findUnique({ where: { email: data.email }, }); if (existingEmail) throw new BadRequestException('Head email already exists'); + const hashedPassword = await bcrypt.hash(data.passwordStr, 10); + return this.prisma.divisional_Head.create({ data: { username: data.username, @@ -87,11 +98,13 @@ export class OfficersService { }); if (existingBadge) throw new BadRequestException('Badge number already exists'); + const existingEmail = await this.prisma.traffic_Officer.findUnique({ where: { email: data.email }, }); if (existingEmail) throw new BadRequestException('Officer email already exists'); + const hashedPassword = await bcrypt.hash(data.passwordStr, 10); return this.prisma.traffic_Officer.create({ data: { @@ -200,4 +213,25 @@ export class OfficersService { currentShift: off.shifts.length > 0 ? off.shifts[0] : null, })); } + + async getAllDivisions() { + return this.prisma.division.findMany({ + include: { + divisionalHead: { + select: { + divisional_Head_Id: true, + name: true, + }, + }, + }, + }); + } + + async getAllDivisionalHeads() { + return this.prisma.divisional_Head.findMany({ + include: { + division: true, + }, + }); + } } From c0d1a6bc1ed0b0888e64a77fb1da6ca2e9d2639d Mon Sep 17 00:00:00 2001 From: venushathi <148311558+vthish@users.noreply.github.com> Date: Thu, 18 Jun 2026 20:40:01 +0530 Subject: [PATCH 10/12] Update CI/CD pipeline for ignore conflict on codes --- .github/workflows/depoly.yml | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/.github/workflows/depoly.yml b/.github/workflows/depoly.yml index 5d7d29d..6025bfd 100644 --- a/.github/workflows/depoly.yml +++ b/.github/workflows/depoly.yml @@ -21,5 +21,7 @@ jobs: key: ${{ secrets.AWS_SSH_KEY }} script: | cd ~/Auto-Ledger - git pull origin development - docker compose up -d --build backend + git fetch origin + git reset --hard origin/development + docker compose build --no-cache backend + docker compose up -d backend From c19dfe16893436f3cb25f3ffaa11e21e49366c64 Mon Sep 17 00:00:00 2001 From: venushathi <148311558+vthish@users.noreply.github.com> Date: Thu, 18 Jun 2026 23:09:12 +0530 Subject: [PATCH 11/12] Update finse service's offense category function --- backend/nest-api/src/fines/fines.service.ts | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/backend/nest-api/src/fines/fines.service.ts b/backend/nest-api/src/fines/fines.service.ts index 35bf7f2..a39f08f 100644 --- a/backend/nest-api/src/fines/fines.service.ts +++ b/backend/nest-api/src/fines/fines.service.ts @@ -197,9 +197,10 @@ export class FinesService { async createOffenseCategory(data: CreateOffenseData, policeAdminId: string) { return this.prisma.offence_Category.create({ data: { - ...data, - points_Value: data.points, + code: data.code, + name: data.name, amount: data.amount, + points_Value: data.points, is_Court_Case: data.isCourtCase, police_Admin_Id: policeAdminId, }, From c4fa8f0d37753cdf88543d675ef33fec066caa2d Mon Sep 17 00:00:00 2001 From: venushathi <148311558+vthish@users.noreply.github.com> Date: Fri, 19 Jun 2026 00:06:20 +0530 Subject: [PATCH 12/12] Update the logic for offense category and update shema code --- backend/nest-api/prisma/schema.prisma | 1 + .../nest-api/src/fines/fines.controller.ts | 17 +++-- backend/nest-api/src/fines/fines.service.ts | 69 +++++++++++++++++-- 3 files changed, 78 insertions(+), 9 deletions(-) diff --git a/backend/nest-api/prisma/schema.prisma b/backend/nest-api/prisma/schema.prisma index b810157..82220c8 100644 --- a/backend/nest-api/prisma/schema.prisma +++ b/backend/nest-api/prisma/schema.prisma @@ -160,6 +160,7 @@ model Offence_Category { name String points_Value Int amount Float + is_Active Boolean @default(true) is_Court_Case Boolean @default(false) police_Admin_Id String policeAdmin Police_Admin @relation(fields: [police_Admin_Id], references: [police_Admin_Id]) diff --git a/backend/nest-api/src/fines/fines.controller.ts b/backend/nest-api/src/fines/fines.controller.ts index ec1b68c..bc030bf 100644 --- a/backend/nest-api/src/fines/fines.controller.ts +++ b/backend/nest-api/src/fines/fines.controller.ts @@ -5,7 +5,6 @@ import { Body, Param, Patch, - Delete, UseGuards, Request, } from '@nestjs/common'; @@ -77,6 +76,16 @@ export class FinesController { return this.finesService.getAllOffenses(); } + @Get('dmt/all-fines') + getAllFinesForDMT() { + return this.finesService.getAllFinesForDMT(); + } + + @Get('dmt/problematic-licenses') + getProblematicLicensesForDMT() { + return this.finesService.getProblematicLicensesForDMT(); + } + @UseGuards(JwtAuthGuard, RolesGuard) @Roles('POLICE_ADMIN') @Post('offenses') @@ -93,9 +102,9 @@ export class FinesController { @UseGuards(JwtAuthGuard, RolesGuard) @Roles('POLICE_ADMIN') - @Delete('offenses/:id') - deleteOffense(@Param('id') id: string) { - return this.finesService.deleteOffenseCategory(id); + @Patch('offenses/:id/toggle') + toggleOffenseStatus(@Param('id') id: string) { + return this.finesService.toggleOffenseStatus(id); } @UseGuards(JwtAuthGuard, RolesGuard) diff --git a/backend/nest-api/src/fines/fines.service.ts b/backend/nest-api/src/fines/fines.service.ts index a39f08f..c9f9aae 100644 --- a/backend/nest-api/src/fines/fines.service.ts +++ b/backend/nest-api/src/fines/fines.service.ts @@ -12,6 +12,7 @@ export interface CreateOffenseData { amount: number; isCourtCase: boolean; } + export interface UpdateOffenseData { name?: string; points?: number; @@ -33,15 +34,18 @@ export class FinesService { where: { traffic_Officer_Id: data.officerId }, }); if (!officer) throw new NotFoundException('Officer not found'); + const license = await this.prisma.driving_License.findUnique({ where: { license_Id: data.licenseId }, }); if (!license) throw new NotFoundException('License not found'); + const offenses = await this.prisma.offence_Category.findMany({ where: { offense_Id: { in: data.offenseIds } }, }); if (offenses.length === 0) throw new BadRequestException('Invalid offenses'); + const isCourtCase = offenses.some((o) => o.is_Court_Case); const fineDueDate = new Date(); fineDueDate.setDate(fineDueDate.getDate() + 14); @@ -56,11 +60,13 @@ export class FinesService { comment: data.comment || null, }, }); + for (const offense of offenses) { await tx.fine_Offence.create({ data: { fine_Id: fine.fine_Id, offense_Id: offense.offense_Id }, }); } + await tx.driving_License.update({ where: { license_Id: data.licenseId }, data: { @@ -70,11 +76,13 @@ export class FinesService { status: 'SUSPENDED', }, }); + if (!isCourtCase) { await tx.temporary_License.create({ data: { license_Id: data.licenseId, expiry_Date: fineDueDate }, }); } + return fine; }); } @@ -84,6 +92,7 @@ export class FinesService { where: { user_Id: userId }, }); if (!license) throw new NotFoundException('License not found'); + return this.prisma.fine.findMany({ where: { license_Id: license.license_Id }, include: { @@ -111,17 +120,21 @@ export class FinesService { const payment = await tx.payment.create({ data: { fine_Id: fineId, amount: amount, status: 'COMPLETED' }, }); + const updatedFine = await tx.fine.update({ where: { fine_Id: fineId }, data: { status: 'PAID' }, }); + await tx.driving_License.update({ where: { license_Id: fine.license_Id }, data: { status: 'ACTIVE' }, }); + await tx.temporary_License.deleteMany({ where: { license_Id: fine.license_Id }, }); + return { message: `Payment successful.`, paymentId: payment.payment_Id, @@ -136,6 +149,7 @@ export class FinesService { }); if (fines.length !== fineIds.length) throw new BadRequestException('Some fines not found'); + for (const fine of fines) { if (fine.status === 'PAID') throw new BadRequestException(`Fine ${fine.fine_Id} already paid`); @@ -144,8 +158,10 @@ export class FinesService { } const licenseId = fines[0].license_Id; + return this.prisma.$transaction(async (tx) => { - const payments = []; + const payments: { payment_Id: string }[] = []; + for (const fineId of fineIds) { const p = await tx.payment.create({ data: { @@ -155,21 +171,25 @@ export class FinesService { }, }); payments.push(p); + await tx.fine.update({ where: { fine_Id: fineId }, data: { status: 'PAID' }, }); } + await tx.driving_License.update({ where: { license_Id: licenseId }, data: { status: 'ACTIVE' }, }); + await tx.temporary_License.deleteMany({ where: { license_Id: licenseId }, }); + return { message: 'Bulk payment successful', - payments: payments.map((p: { payment_Id: string }) => p.payment_Id), + payments: payments.map((p) => p.payment_Id), }; }); } @@ -179,11 +199,13 @@ export class FinesService { where: { fine_Id: fineId }, }); if (!fine) throw new NotFoundException('Fine not found'); + return this.prisma.$transaction(async (tx) => { await tx.fine.update({ where: { fine_Id: fineId }, data: { status: 'PAID' }, }); + return tx.driving_License.update({ where: { license_Id: fine.license_Id }, data: { status: verdict, points: verdict === 'ACTIVE' ? 24 : 0 }, @@ -194,6 +216,7 @@ export class FinesService { async getAllOffenses() { return this.prisma.offence_Category.findMany({ orderBy: { code: 'asc' } }); } + async createOffenseCategory(data: CreateOffenseData, policeAdminId: string) { return this.prisma.offence_Category.create({ data: { @@ -206,6 +229,7 @@ export class FinesService { }, }); } + async updateOffenseCategory(id: string, data: UpdateOffenseData) { return this.prisma.offence_Category.update({ where: { offense_Id: id }, @@ -217,8 +241,40 @@ export class FinesService { }, }); } - async deleteOffenseCategory(id: string) { - return this.prisma.offence_Category.delete({ where: { offense_Id: id } }); + + async toggleOffenseStatus(id: string) { + const offense = await this.prisma.offence_Category.findUnique({ + where: { offense_Id: id }, + }); + if (!offense) throw new NotFoundException('Offense not found'); + + return this.prisma.offence_Category.update({ + where: { offense_Id: id }, + data: { is_Active: !offense.is_Active }, + }); + } + + async getAllFinesForDMT() { + return this.prisma.fine.findMany({ + include: { + license: { + select: { license_No: true, nic_No: true, full_Name: true }, + }, + offenses: { + include: { offenceCategory: true }, + }, + }, + orderBy: { issue_At: 'desc' }, + }); + } + + async getProblematicLicensesForDMT() { + return this.prisma.driving_License.findMany({ + where: { + status: { in: ['SUSPENDED', 'REVOKED'] }, + }, + orderBy: { points: 'asc' }, + }); } async getCourtCasesByDH(headId: string) { @@ -227,6 +283,7 @@ export class FinesService { select: { traffic_Officer_Id: true }, }); const officerIds = officers.map((o) => o.traffic_Officer_Id); + return this.prisma.fine.findMany({ where: { status: 'OVERDUE', traffic_Officer_Id: { in: officerIds } }, include: { @@ -254,11 +311,13 @@ export class FinesService { }, }, }); + const officerIds = officers.map((o) => o.traffic_Officer_Id); const fines = await this.prisma.fine.findMany({ where: { traffic_Officer_Id: { in: officerIds } }, include: { payment: true }, }); + return { totalOfficers: officers.length, activeOfficersOnDuty: officers.filter((o) => o.shifts.length > 0).length, @@ -267,7 +326,7 @@ export class FinesService { overdueCourtCases: fines.filter((f) => f.status === 'OVERDUE').length, totalRevenue: fines .filter((f) => f.status === 'PAID' && f.payment) - .reduce((sum, f) => sum + f.payment.amount, 0), + .reduce((sum, f) => sum + (f.payment?.amount || 0), 0), }; } }