From a17975189a2e74674cdac94b5451d17d7c764a63 Mon Sep 17 00:00:00 2001 From: "Salawu O. Joseph" Date: Wed, 30 Apr 2025 06:48:42 +0100 Subject: [PATCH 1/2] registration route working --- package-lock.json | 715 ++++++++++++++++-- package.json | 21 +- src/app.controller.ts | 12 - src/app.module.ts | 16 +- src/app.service.ts | 8 - src/auth/auth.controller.ts | 27 + src/auth/auth.module.ts | 20 + src/auth/auth.service.ts | 38 + src/auth/dto/authdto.ts | 15 + src/common/repository/adminRepository.ts | 110 +++ src/common/repository/postgres.repository.ts | 65 -- src/config/config.service.ts | 18 +- src/config/logger.service.ts | 6 +- src/database/datasource.ts | 26 +- .../entities/admin-notification.entity.ts | 36 +- src/database/entities/admin.entity.ts | 54 +- src/database/entities/audit-log.entity.ts | 40 +- src/database/entities/base.entity.ts | 20 + src/database/entities/role.entity.ts | 28 +- .../1745272202322-createAdminServiceTables.ts | 26 - src/helpers/utils.ts | 71 ++ src/ioc.ts | 7 + src/main.ts | 8 +- tsconfig.json | 3 +- 24 files changed, 1117 insertions(+), 273 deletions(-) delete mode 100644 src/app.controller.ts delete mode 100644 src/app.service.ts create mode 100644 src/auth/auth.controller.ts create mode 100644 src/auth/auth.module.ts create mode 100644 src/auth/auth.service.ts create mode 100644 src/auth/dto/authdto.ts create mode 100644 src/common/repository/adminRepository.ts delete mode 100644 src/common/repository/postgres.repository.ts create mode 100644 src/database/entities/base.entity.ts delete mode 100644 src/database/migrations/1745272202322-createAdminServiceTables.ts create mode 100644 src/helpers/utils.ts create mode 100644 src/ioc.ts diff --git a/package-lock.json b/package-lock.json index df54c81..29fc50e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -15,15 +15,23 @@ "@nestjs/platform-express": "^10.0.0", "@nestjs/swagger": "^11.1.3", "@nestjs/typeorm": "^11.0.0", + "bcrypt": "^5.1.1", + "bcryptjs": "^3.0.2", + "chance": "^1.1.12", + "class-transformer": "^0.5.1", + "class-validator": "^0.14.1", "compression": "^1.8.0", "dotenv": "^16.5.0", "express-rate-limit": "^7.5.0", "helmet": "^8.1.0", - "pg": "^8.14.1", - "reflect-metadata": "^0.1.13", + "jsonwebtoken": "^9.0.2", + "pg": "^8.15.6", + "reflect-metadata": "^0.1.14", "rxjs": "^7.8.1", "swagger-ui-express": "^5.0.1", + "typedi": "^0.10.0", "typeorm": "^0.3.22", + "typeorm-typedi-extensions": "^0.4.1", "winston": "^3.17.0", "winston-daily-rotate-file": "^5.0.0" }, @@ -759,7 +767,7 @@ "version": "0.8.1", "resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz", "integrity": "sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==", - "dev": true, + "devOptional": true, "license": "MIT", "dependencies": { "@jridgewell/trace-mapping": "0.3.9" @@ -772,7 +780,7 @@ "version": "0.3.9", "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz", "integrity": "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==", - "dev": true, + "devOptional": true, "license": "MIT", "dependencies": { "@jridgewell/resolve-uri": "^3.0.3", @@ -1533,7 +1541,7 @@ "version": "3.1.2", "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", - "dev": true, + "devOptional": true, "license": "MIT", "engines": { "node": ">=6.0.0" @@ -1564,7 +1572,7 @@ "version": "1.5.0", "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.0.tgz", "integrity": "sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==", - "dev": true, + "devOptional": true, "license": "MIT" }, "node_modules/@jridgewell/trace-mapping": { @@ -1600,6 +1608,50 @@ "node": ">=8" } }, + "node_modules/@mapbox/node-pre-gyp": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/@mapbox/node-pre-gyp/-/node-pre-gyp-1.0.11.tgz", + "integrity": "sha512-Yhlar6v9WQgUp/He7BdgzOz8lqMQ8sU+jkCq7Wx8Myc5YFJLbEe7lgui/V7G1qB1DJykHSGwreceSaD60Y0PUQ==", + "license": "BSD-3-Clause", + "dependencies": { + "detect-libc": "^2.0.0", + "https-proxy-agent": "^5.0.0", + "make-dir": "^3.1.0", + "node-fetch": "^2.6.7", + "nopt": "^5.0.0", + "npmlog": "^5.0.1", + "rimraf": "^3.0.2", + "semver": "^7.3.5", + "tar": "^6.1.11" + }, + "bin": { + "node-pre-gyp": "bin/node-pre-gyp" + } + }, + "node_modules/@mapbox/node-pre-gyp/node_modules/make-dir": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz", + "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==", + "license": "MIT", + "dependencies": { + "semver": "^6.0.0" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@mapbox/node-pre-gyp/node_modules/make-dir/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + } + }, "node_modules/@microsoft/tsdoc": { "version": "0.15.1", "resolved": "https://registry.npmjs.org/@microsoft/tsdoc/-/tsdoc-0.15.1.tgz", @@ -2102,28 +2154,28 @@ "version": "1.0.11", "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.11.tgz", "integrity": "sha512-DcRjDCujK/kCk/cUe8Xz8ZSpm8mS3mNNpta+jGCA6USEDfktlNvm1+IuZ9eTcDbNk41BHwpHHeW+N1lKCz4zOw==", - "dev": true, + "devOptional": true, "license": "MIT" }, "node_modules/@tsconfig/node12": { "version": "1.0.11", "resolved": "https://registry.npmjs.org/@tsconfig/node12/-/node12-1.0.11.tgz", "integrity": "sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==", - "dev": true, + "devOptional": true, "license": "MIT" }, "node_modules/@tsconfig/node14": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.3.tgz", "integrity": "sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==", - "dev": true, + "devOptional": true, "license": "MIT" }, "node_modules/@tsconfig/node16": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.4.tgz", "integrity": "sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA==", - "dev": true, + "devOptional": true, "license": "MIT" }, "node_modules/@types/babel__core": { @@ -2364,7 +2416,7 @@ "version": "20.17.30", "resolved": "https://registry.npmjs.org/@types/node/-/node-20.17.30.tgz", "integrity": "sha512-7zf4YyHA+jvBNfVrk2Gtvs6x7E8V+YDW05bNfG2XkWDJfYRXrTiP/DsB2zSYTaHX0bGIujTBQdMVAhb+j7mwpg==", - "dev": true, + "devOptional": true, "license": "MIT", "dependencies": { "undici-types": "~6.19.2" @@ -2450,6 +2502,12 @@ "integrity": "sha512-6WaYesThRMCl19iryMYP7/x2OVgCtbIVflDGFpWnb9irXI3UjYE4AzmYuiUKY1AJstGijoY+MgUszMgRxIYTYw==", "license": "MIT" }, + "node_modules/@types/validator": { + "version": "13.15.0", + "resolved": "https://registry.npmjs.org/@types/validator/-/validator-13.15.0.tgz", + "integrity": "sha512-nh7nrWhLr6CBq9ldtw0wx+z9wKnnv/uTVLA9g/3/TcOYxbpOSZE+MhKPmWqU+K0NvThjhv12uD8MuqijB0WzEA==", + "license": "MIT" + }, "node_modules/@types/yargs": { "version": "17.0.33", "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.33.tgz", @@ -2847,6 +2905,12 @@ "dev": true, "license": "Apache-2.0" }, + "node_modules/abbrev": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz", + "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==", + "license": "ISC" + }, "node_modules/accepts": { "version": "1.3.8", "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", @@ -2864,7 +2928,7 @@ "version": "8.14.1", "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.14.1.tgz", "integrity": "sha512-OvQ/2pUDKmgfCg++xsTX1wGxfTaszcHVcTctW4UJB4hibJx2HXxxO5UmVgyjMa+ZDsiaf5wWLXYpRWMmBI0QHg==", - "dev": true, + "devOptional": true, "license": "MIT", "bin": { "acorn": "bin/acorn" @@ -2887,7 +2951,7 @@ "version": "8.3.4", "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.3.4.tgz", "integrity": "sha512-ueEepnujpqee2o5aIYnvHU6C0A42MNdsIDeqy5BydrkuC5R1ZuUFnm27EeFJGoEHJQgn3uleRvmTXaJgfXbt4g==", - "dev": true, + "devOptional": true, "license": "MIT", "dependencies": { "acorn": "^8.11.0" @@ -2896,6 +2960,18 @@ "node": ">=0.4.0" } }, + "node_modules/agent-base": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", + "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", + "license": "MIT", + "dependencies": { + "debug": "4" + }, + "engines": { + "node": ">= 6.0.0" + } + }, "node_modules/ajv": { "version": "8.12.0", "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.12.0.tgz", @@ -3058,11 +3134,45 @@ "integrity": "sha512-klpgFSWLW1ZEs8svjfb7g4qWY0YS5imI82dTg+QahUvJ8YqAY0P10Uk8tTyh9ZGuYEZEMaeJYCF5BFuX552hsw==", "license": "MIT" }, + "node_modules/aproba": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/aproba/-/aproba-2.0.0.tgz", + "integrity": "sha512-lYe4Gx7QT+MKGbDsA+Z+he/Wtef0BiwDOlK/XkBrdfsh9J/jPPXbX0tE9x9cl27Tmu5gg3QUbUrQYa/y+KOHPQ==", + "license": "ISC" + }, + "node_modules/are-we-there-yet": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-2.0.0.tgz", + "integrity": "sha512-Ci/qENmwHnsYo9xKIcUJN5LeDKdJ6R1Z1j9V/J5wyq8nh/mYPEpIKJbBZXtZjG04HiK7zV/p6Vs9952MrMeUIw==", + "deprecated": "This package is no longer supported.", + "license": "ISC", + "dependencies": { + "delegates": "^1.0.0", + "readable-stream": "^3.6.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/are-we-there-yet/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==", + "license": "MIT", + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } + }, "node_modules/arg": { "version": "4.1.3", "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz", "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==", - "dev": true, + "devOptional": true, "license": "MIT" }, "node_modules/argparse": { @@ -3266,6 +3376,29 @@ ], "license": "MIT" }, + "node_modules/bcrypt": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/bcrypt/-/bcrypt-5.1.1.tgz", + "integrity": "sha512-AGBHOG5hPYZ5Xl9KXzU5iKq9516yEmvCKDg3ecP5kX2aB6UqTeXZxk2ELnDgDm6BQSMlLt9rDB4LoSMx0rYwww==", + "hasInstallScript": true, + "license": "MIT", + "dependencies": { + "@mapbox/node-pre-gyp": "^1.0.11", + "node-addon-api": "^5.0.0" + }, + "engines": { + "node": ">= 10.0.0" + } + }, + "node_modules/bcryptjs": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/bcryptjs/-/bcryptjs-3.0.2.tgz", + "integrity": "sha512-k38b3XOZKv60C4E2hVsXTolJWfkGRMbILBIe2IBITXciy5bOsTKot5kDrf3ZfufQtQOUN5mXceUEpU1rTl9Uog==", + "license": "BSD-3-Clause", + "bin": { + "bcrypt": "bin/bcrypt" + } + }, "node_modules/binary-extensions": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz", @@ -3448,6 +3581,12 @@ "ieee754": "^1.1.13" } }, + "node_modules/buffer-equal-constant-time": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz", + "integrity": "sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA==", + "license": "BSD-3-Clause" + }, "node_modules/buffer-from": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", @@ -3579,6 +3718,12 @@ "url": "https://github.com/chalk/chalk?sponsor=1" } }, + "node_modules/chance": { + "version": "1.1.12", + "resolved": "https://registry.npmjs.org/chance/-/chance-1.1.12.tgz", + "integrity": "sha512-vVBIGQVnwtUG+SYe0ge+3MvF78cvSpuCOEUJr7sVEk2vSBuMW6OXNJjSzdtzrlxNUEaoqH2GBd5Y/+18BEB01Q==", + "license": "MIT" + }, "node_modules/char-regex": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/char-regex/-/char-regex-1.0.2.tgz", @@ -3621,6 +3766,15 @@ "fsevents": "~2.3.2" } }, + "node_modules/chownr": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/chownr/-/chownr-2.0.0.tgz", + "integrity": "sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==", + "license": "ISC", + "engines": { + "node": ">=10" + } + }, "node_modules/chrome-trace-event": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/chrome-trace-event/-/chrome-trace-event-1.0.4.tgz", @@ -3654,6 +3808,23 @@ "dev": true, "license": "MIT" }, + "node_modules/class-transformer": { + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/class-transformer/-/class-transformer-0.5.1.tgz", + "integrity": "sha512-SQa1Ws6hUbfC98vKGxZH3KFY0Y1lm5Zm0SY8XX9zbK7FJCyVEac3ATW0RIpwzW+oOfmHE5PMPufDG9hCfoEOMw==", + "license": "MIT" + }, + "node_modules/class-validator": { + "version": "0.14.1", + "resolved": "https://registry.npmjs.org/class-validator/-/class-validator-0.14.1.tgz", + "integrity": "sha512-2VEG9JICxIqTpoK1eMzZqaV+u/EiwEJkMGzTrZf6sU/fwsnOITVgYJ8yojSy6CaXtO9V0Cc6ZQZ8h8m4UBuLwQ==", + "license": "MIT", + "dependencies": { + "@types/validator": "^13.11.8", + "libphonenumber-js": "^1.10.53", + "validator": "^13.9.0" + } + }, "node_modules/cli-cursor": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-3.1.0.tgz", @@ -3803,6 +3974,15 @@ "simple-swizzle": "^0.2.2" } }, + "node_modules/color-support": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-support/-/color-support-1.1.3.tgz", + "integrity": "sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg==", + "license": "ISC", + "bin": { + "color-support": "bin.js" + } + }, "node_modules/color/node_modules/color-convert": { "version": "1.9.3", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", @@ -3936,7 +4116,6 @@ "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", - "dev": true, "license": "MIT" }, "node_modules/concat-stream": { @@ -3960,6 +4139,12 @@ "integrity": "sha512-9vAdYbHj6x2fLKC4+oPH0kFzY/orMZyG2Aj+kNylHxKGJ/Ed4dpNyAQYwJOdqO4zdM7XpVHmyejQDcQHrnuXbw==", "license": "MIT" }, + "node_modules/console-control-strings": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz", + "integrity": "sha512-ty/fTekppD2fIwRvnZAVdeOiGd1c7YXEixbgJTNzqcxJWKQnjJ/V1bNEEE6hygpM3WjwHFUVK6HTjWSzV4a8sQ==", + "license": "ISC" + }, "node_modules/content-disposition": { "version": "0.5.4", "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz", @@ -4082,7 +4267,7 @@ "version": "1.1.1", "resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz", "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==", - "dev": true, + "devOptional": true, "license": "MIT" }, "node_modules/cross-spawn": { @@ -4195,6 +4380,12 @@ "node": ">=0.4.0" } }, + "node_modules/delegates": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz", + "integrity": "sha512-bd2L678uiWATM6m5Z1VzNCErI3jiGzt6HGY8OVICs40JQq/HALfbyNJmp0UDakEY4pMMaN0Ly5om/B1VI/+xfQ==", + "license": "MIT" + }, "node_modules/depd": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", @@ -4214,6 +4405,15 @@ "npm": "1.2.8000 || >= 1.4.16" } }, + "node_modules/detect-libc": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.0.4.tgz", + "integrity": "sha512-3UDv+G9CsCKO1WKMGw9fwq/SWJYbI0c5Y7LU1AXYoDdbhE2AHQ6N6Nb34sG8Fj7T5APy8qXDCKuuIHd1BR0tVA==", + "license": "Apache-2.0", + "engines": { + "node": ">=8" + } + }, "node_modules/detect-newline": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/detect-newline/-/detect-newline-3.1.0.tgz", @@ -4239,7 +4439,7 @@ "version": "4.0.2", "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", - "dev": true, + "devOptional": true, "license": "BSD-3-Clause", "engines": { "node": ">=0.3.1" @@ -4328,6 +4528,15 @@ "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==", "license": "MIT" }, + "node_modules/ecdsa-sig-formatter": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz", + "integrity": "sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==", + "license": "Apache-2.0", + "dependencies": { + "safe-buffer": "^5.0.1" + } + }, "node_modules/ee-first": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", @@ -5312,6 +5521,36 @@ "node": ">=12" } }, + "node_modules/fs-minipass": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-2.1.0.tgz", + "integrity": "sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg==", + "license": "ISC", + "dependencies": { + "minipass": "^3.0.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/fs-minipass/node_modules/minipass": { + "version": "3.3.6", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", + "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", + "license": "ISC", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/fs-minipass/node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "license": "ISC" + }, "node_modules/fs-monkey": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/fs-monkey/-/fs-monkey-1.0.6.tgz", @@ -5323,7 +5562,6 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", - "dev": true, "license": "ISC" }, "node_modules/fsevents": { @@ -5350,6 +5588,33 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/gauge": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/gauge/-/gauge-3.0.2.tgz", + "integrity": "sha512-+5J6MS/5XksCuXq++uFRsnUd7Ovu1XenbeuIuNRJxYWjgQbPuFhT14lAvsWfqfAmnwluf1OwMjz39HjfLPci0Q==", + "deprecated": "This package is no longer supported.", + "license": "ISC", + "dependencies": { + "aproba": "^1.0.3 || ^2.0.0", + "color-support": "^1.1.2", + "console-control-strings": "^1.0.0", + "has-unicode": "^2.0.1", + "object-assign": "^4.1.1", + "signal-exit": "^3.0.0", + "string-width": "^4.2.3", + "strip-ansi": "^6.0.1", + "wide-align": "^1.1.2" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/gauge/node_modules/signal-exit": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", + "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", + "license": "ISC" + }, "node_modules/gensync": { "version": "1.0.0-beta.2", "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", @@ -5607,6 +5872,12 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/has-unicode": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz", + "integrity": "sha512-8Rf9Y83NBReMnx0gFzA8JImQACstCYWUplepDa9xprwwtmgEZUF0h/i5xSA625zB/I37EtrswSST6OXxwaaIJQ==", + "license": "ISC" + }, "node_modules/hasown": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", @@ -5661,6 +5932,19 @@ "node": ">= 0.8" } }, + "node_modules/https-proxy-agent": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz", + "integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==", + "license": "MIT", + "dependencies": { + "agent-base": "6", + "debug": "4" + }, + "engines": { + "node": ">= 6" + } + }, "node_modules/human-signals": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz", @@ -5765,7 +6049,6 @@ "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", "deprecated": "This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful.", - "dev": true, "license": "ISC", "dependencies": { "once": "^1.3.0", @@ -6909,6 +7192,49 @@ "graceful-fs": "^4.1.6" } }, + "node_modules/jsonwebtoken": { + "version": "9.0.2", + "resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-9.0.2.tgz", + "integrity": "sha512-PRp66vJ865SSqOlgqS8hujT5U4AOgMfhrwYIuIhfKaoSCZcirrmASQr8CX7cUg+RMih+hgznrjp99o+W4pJLHQ==", + "license": "MIT", + "dependencies": { + "jws": "^3.2.2", + "lodash.includes": "^4.3.0", + "lodash.isboolean": "^3.0.3", + "lodash.isinteger": "^4.0.4", + "lodash.isnumber": "^3.0.3", + "lodash.isplainobject": "^4.0.6", + "lodash.isstring": "^4.0.1", + "lodash.once": "^4.0.0", + "ms": "^2.1.1", + "semver": "^7.5.4" + }, + "engines": { + "node": ">=12", + "npm": ">=6" + } + }, + "node_modules/jwa": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/jwa/-/jwa-1.4.1.tgz", + "integrity": "sha512-qiLX/xhEEFKUAJ6FiBMbes3w9ATzyk5W7Hvzpa/SLYdxNtng+gcurvrI7TbACjIXlsJyr05/S1oUhZrc63evQA==", + "license": "MIT", + "dependencies": { + "buffer-equal-constant-time": "1.0.1", + "ecdsa-sig-formatter": "1.0.11", + "safe-buffer": "^5.0.1" + } + }, + "node_modules/jws": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/jws/-/jws-3.2.2.tgz", + "integrity": "sha512-YHlZCB6lMTllWDtSPHz/ZXTsi8S00usEV6v1tjq8tOUZzw7DpSDWVXjXDre6ed1w/pd495ODpHZYSdkRTsa0HA==", + "license": "MIT", + "dependencies": { + "jwa": "^1.4.1", + "safe-buffer": "^5.0.1" + } + }, "node_modules/keyv": { "version": "4.5.4", "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", @@ -6959,6 +7285,12 @@ "node": ">= 0.8.0" } }, + "node_modules/libphonenumber-js": { + "version": "1.12.7", + "resolved": "https://registry.npmjs.org/libphonenumber-js/-/libphonenumber-js-1.12.7.tgz", + "integrity": "sha512-0nYZSNj/QEikyhcM5RZFXGlCB/mr4PVamnT1C2sKBnDDTYndrvbybYjvg+PMqAndQHlLbwQ3socolnL3WWTUFA==", + "license": "MIT" + }, "node_modules/lines-and-columns": { "version": "1.2.4", "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", @@ -6998,6 +7330,42 @@ "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", "license": "MIT" }, + "node_modules/lodash.includes": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/lodash.includes/-/lodash.includes-4.3.0.tgz", + "integrity": "sha512-W3Bx6mdkRTGtlJISOvVD/lbqjTlPPUDTMnlXZFnVwi9NKJ6tiAk6LVdlhZMm17VZisqhKcgzpO5Wz91PCt5b0w==", + "license": "MIT" + }, + "node_modules/lodash.isboolean": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/lodash.isboolean/-/lodash.isboolean-3.0.3.tgz", + "integrity": "sha512-Bz5mupy2SVbPHURB98VAcw+aHh4vRV5IPNhILUCsOzRmsTmSQ17jIuqopAentWoehktxGd9e/hbIXq980/1QJg==", + "license": "MIT" + }, + "node_modules/lodash.isinteger": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/lodash.isinteger/-/lodash.isinteger-4.0.4.tgz", + "integrity": "sha512-DBwtEWN2caHQ9/imiNeEA5ys1JoRtRfY3d7V9wkqtbycnAmTvRRmbHKDV4a0EYc678/dia0jrte4tjYwVBaZUA==", + "license": "MIT" + }, + "node_modules/lodash.isnumber": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/lodash.isnumber/-/lodash.isnumber-3.0.3.tgz", + "integrity": "sha512-QYqzpfwO3/CWf3XP+Z+tkQsfaLL/EnUlXWVkIk5FUPc4sBdTehEqZONuyRt2P67PXAk+NXmTBcc97zw9t1FQrw==", + "license": "MIT" + }, + "node_modules/lodash.isplainobject": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz", + "integrity": "sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA==", + "license": "MIT" + }, + "node_modules/lodash.isstring": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/lodash.isstring/-/lodash.isstring-4.0.1.tgz", + "integrity": "sha512-0wJxfxH1wgO3GrbuP+dTTk7op+6L41QCXbGINEmD+ny/G/eCqGzxyCsh7159S+mgDDcoarnBw6PC1PS5+wUGgw==", + "license": "MIT" + }, "node_modules/lodash.memoize": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-4.1.2.tgz", @@ -7012,6 +7380,12 @@ "dev": true, "license": "MIT" }, + "node_modules/lodash.once": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/lodash.once/-/lodash.once-4.1.1.tgz", + "integrity": "sha512-Sb487aTOCr9drQVL8pIxOzVhafOjZN9UU54hiN8PU3uAiSV7lx1yYNpbNmex2PK6dSJoNTSJUUswT651yww3Mg==", + "license": "MIT" + }, "node_modules/log-symbols": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz", @@ -7098,7 +7472,7 @@ "version": "1.3.6", "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==", - "dev": true, + "devOptional": true, "license": "ISC" }, "node_modules/makeerror": { @@ -7281,6 +7655,37 @@ "node": ">=16 || 14 >=14.17" } }, + "node_modules/minizlib": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-2.1.2.tgz", + "integrity": "sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg==", + "license": "MIT", + "dependencies": { + "minipass": "^3.0.0", + "yallist": "^4.0.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/minizlib/node_modules/minipass": { + "version": "3.3.6", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", + "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", + "license": "ISC", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/minizlib/node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "license": "ISC" + }, "node_modules/mkdirp": { "version": "0.5.6", "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz", @@ -7363,6 +7768,12 @@ "dev": true, "license": "MIT" }, + "node_modules/node-addon-api": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-5.1.0.tgz", + "integrity": "sha512-eh0GgfEkpnoWDq+VY8OyvYhFEzBk6jIYbRKdIlyTiAXIVJ8PyBaKb0rp7oDtoddbdoHWhq8wwr+XZ81F1rpNdA==", + "license": "MIT" + }, "node_modules/node-emoji": { "version": "1.11.0", "resolved": "https://registry.npmjs.org/node-emoji/-/node-emoji-1.11.0.tgz", @@ -7407,6 +7818,21 @@ "dev": true, "license": "MIT" }, + "node_modules/nopt": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/nopt/-/nopt-5.0.0.tgz", + "integrity": "sha512-Tbj67rffqceeLpcRXrT7vKAN8CwfPeIBgM7E6iBkmKLV7bEMwpGgYLGv0jACUsECaa/vuxP0IjEont6umdMgtQ==", + "license": "ISC", + "dependencies": { + "abbrev": "1" + }, + "bin": { + "nopt": "bin/nopt.js" + }, + "engines": { + "node": ">=6" + } + }, "node_modules/normalize-path": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", @@ -7430,6 +7856,19 @@ "node": ">=8" } }, + "node_modules/npmlog": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-5.0.1.tgz", + "integrity": "sha512-AqZtDUWOMKs1G/8lwylVjrdYgqA4d9nu8hc+0gzRxlDb1I10+FHBGMXs6aiQHFdCUUlqH99MUMuLfzWDNDtfxw==", + "deprecated": "This package is no longer supported.", + "license": "ISC", + "dependencies": { + "are-we-there-yet": "^2.0.0", + "console-control-strings": "^1.1.0", + "gauge": "^3.0.0", + "set-blocking": "^2.0.0" + } + }, "node_modules/object-assign": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", @@ -7485,7 +7924,6 @@ "version": "1.4.0", "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", - "dev": true, "license": "ISC", "dependencies": { "wrappy": "1" @@ -7671,7 +8109,6 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", - "dev": true, "license": "MIT", "engines": { "node": ">=0.10.0" @@ -7732,14 +8169,14 @@ } }, "node_modules/pg": { - "version": "8.14.1", - "resolved": "https://registry.npmjs.org/pg/-/pg-8.14.1.tgz", - "integrity": "sha512-0TdbqfjwIun9Fm/r89oB7RFQ0bLgduAhiIqIXOsyKoiC/L54DbuAAzIEN/9Op0f1Po9X7iCPXGoa/Ah+2aI8Xw==", + "version": "8.15.6", + "resolved": "https://registry.npmjs.org/pg/-/pg-8.15.6.tgz", + "integrity": "sha512-yvao7YI3GdmmrslNVsZgx9PfntfWrnXwtR+K/DjI0I/sTKif4Z623um+sjVZ1hk5670B+ODjvHDAckKdjmPTsg==", "license": "MIT", "dependencies": { - "pg-connection-string": "^2.7.0", - "pg-pool": "^3.8.0", - "pg-protocol": "^1.8.0", + "pg-connection-string": "^2.8.5", + "pg-pool": "^3.9.6", + "pg-protocol": "^1.9.5", "pg-types": "^2.1.0", "pgpass": "1.x" }, @@ -7747,7 +8184,7 @@ "node": ">= 8.0.0" }, "optionalDependencies": { - "pg-cloudflare": "^1.1.1" + "pg-cloudflare": "^1.2.5" }, "peerDependencies": { "pg-native": ">=3.0.1" @@ -7759,16 +8196,16 @@ } }, "node_modules/pg-cloudflare": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/pg-cloudflare/-/pg-cloudflare-1.1.1.tgz", - "integrity": "sha512-xWPagP/4B6BgFO+EKz3JONXv3YDgvkbVrGw2mTo3D6tVDQRh1e7cqVGvyR3BE+eQgAvx1XhW/iEASj4/jCWl3Q==", + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/pg-cloudflare/-/pg-cloudflare-1.2.5.tgz", + "integrity": "sha512-OOX22Vt0vOSRrdoUPKJ8Wi2OpE/o/h9T8X1s4qSkCedbNah9ei2W2765be8iMVxQUsvgT7zIAT2eIa9fs5+vtg==", "license": "MIT", "optional": true }, "node_modules/pg-connection-string": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/pg-connection-string/-/pg-connection-string-2.7.0.tgz", - "integrity": "sha512-PI2W9mv53rXJQEOb8xNR8lH7Hr+EKa6oJa38zsK0S/ky2er16ios1wLKhZyxzD7jUReiWokc9WK5nxSnC7W1TA==", + "version": "2.8.5", + "resolved": "https://registry.npmjs.org/pg-connection-string/-/pg-connection-string-2.8.5.tgz", + "integrity": "sha512-Ni8FuZ8yAF+sWZzojvtLE2b03cqjO5jNULcHFfM9ZZ0/JXrgom5pBREbtnAw7oxsxJqHw9Nz/XWORUEL3/IFow==", "license": "MIT" }, "node_modules/pg-int8": { @@ -7781,18 +8218,18 @@ } }, "node_modules/pg-pool": { - "version": "3.8.0", - "resolved": "https://registry.npmjs.org/pg-pool/-/pg-pool-3.8.0.tgz", - "integrity": "sha512-VBw3jiVm6ZOdLBTIcXLNdSotb6Iy3uOCwDGFAksZCXmi10nyRvnP2v3jl4d+IsLYRyXf6o9hIm/ZtUzlByNUdw==", + "version": "3.9.6", + "resolved": "https://registry.npmjs.org/pg-pool/-/pg-pool-3.9.6.tgz", + "integrity": "sha512-rFen0G7adh1YmgvrmE5IPIqbb+IgEzENUm+tzm6MLLDSlPRoZVhzU1WdML9PV2W5GOdRA9qBKURlbt1OsXOsPw==", "license": "MIT", "peerDependencies": { "pg": ">=8.0" } }, "node_modules/pg-protocol": { - "version": "1.8.0", - "resolved": "https://registry.npmjs.org/pg-protocol/-/pg-protocol-1.8.0.tgz", - "integrity": "sha512-jvuYlEkL03NRvOoyoRktBK7+qU5kOvlAwvmrH8sr3wbLrOdVWsRxQfz8mMy9sZFsqJ1hEWNfdWKI4SAmoL+j7g==", + "version": "1.9.5", + "resolved": "https://registry.npmjs.org/pg-protocol/-/pg-protocol-1.9.5.tgz", + "integrity": "sha512-DYTWtWpfd5FOro3UnAfwvhD8jh59r2ig8bPtc9H8Ds7MscE/9NYruUQWFAOuraRl29jwcT2kyMFQ3MxeaVjUhg==", "license": "MIT" }, "node_modules/pg-types": { @@ -8355,7 +8792,6 @@ "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", "deprecated": "Rimraf versions prior to v4 are no longer supported", - "dev": true, "license": "ISC", "dependencies": { "glob": "^7.1.3" @@ -8371,7 +8807,6 @@ "version": "1.1.11", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "dev": true, "license": "MIT", "dependencies": { "balanced-match": "^1.0.0", @@ -8383,7 +8818,6 @@ "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", "deprecated": "Glob versions prior to v9 are no longer supported", - "dev": true, "license": "ISC", "dependencies": { "fs.realpath": "^1.0.0", @@ -8404,7 +8838,6 @@ "version": "3.1.2", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "dev": true, "license": "ISC", "dependencies": { "brace-expansion": "^1.1.7" @@ -8548,7 +8981,6 @@ "version": "7.7.1", "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.1.tgz", "integrity": "sha512-hlq8tAfn0m/61p4BVRcPzIGr6LKiMwo4VM6dGi6pt4qcRkmNzTcWq6eCEjEh+qXjkMDvPlOFFSGwQjoEa6gyMA==", - "dev": true, "license": "ISC", "bin": { "semver": "bin/semver.js" @@ -8630,6 +9062,12 @@ "node": ">= 0.8.0" } }, + "node_modules/set-blocking": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", + "integrity": "sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==", + "license": "ISC" + }, "node_modules/set-function-length": { "version": "1.2.2", "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz", @@ -9168,6 +9606,50 @@ "node": ">=6" } }, + "node_modules/tar": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/tar/-/tar-6.2.1.tgz", + "integrity": "sha512-DZ4yORTwrbTj/7MZYq2w+/ZFdI6OZ/f9SFHR+71gIVUZhOQPHzVCLpvRnPgyaMpfWxxk/4ONva3GQSyNIKRv6A==", + "license": "ISC", + "dependencies": { + "chownr": "^2.0.0", + "fs-minipass": "^2.0.0", + "minipass": "^5.0.0", + "minizlib": "^2.1.1", + "mkdirp": "^1.0.3", + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/tar/node_modules/minipass": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-5.0.0.tgz", + "integrity": "sha512-3FnjYuehv9k6ovOEbyOswadCDPX1piCfhV8ncmYtHOjuPwylVWsghTLo7rabjC3Rx5xD4HDx8Wm1xnMF7S5qFQ==", + "license": "ISC", + "engines": { + "node": ">=8" + } + }, + "node_modules/tar/node_modules/mkdirp": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", + "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", + "license": "MIT", + "bin": { + "mkdirp": "bin/cmd.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/tar/node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "license": "ISC" + }, "node_modules/terser": { "version": "5.39.0", "resolved": "https://registry.npmjs.org/terser/-/terser-5.39.0.tgz", @@ -9529,7 +10011,7 @@ "version": "10.9.2", "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.2.tgz", "integrity": "sha512-f0FFpIdcHgn8zcPSbf1dRevwt047YMnaiJM3u2w2RewrB+fob/zePZcrOyQoLMMO7aBIddLcQIEK5dYjkLnGrQ==", - "dev": true, + "devOptional": true, "license": "MIT", "dependencies": { "@cspotcode/source-map-support": "^0.8.0", @@ -9671,6 +10153,12 @@ "integrity": "sha512-/aCDEGatGvZ2BIk+HmLf4ifCJFwvKFNb9/JeZPMulfgFracn9QFcAf5GO8B/mweUjSoblS5In0cWhqpfs/5PQA==", "license": "MIT" }, + "node_modules/typedi": { + "version": "0.10.0", + "resolved": "https://registry.npmjs.org/typedi/-/typedi-0.10.0.tgz", + "integrity": "sha512-v3UJF8xm68BBj6AF4oQML3ikrfK2c9EmZUyLOfShpJuItAqVBHWP/KtpGinkSsIiP6EZyyb6Z3NXyW9dgS9X1w==", + "license": "MIT" + }, "node_modules/typeorm": { "version": "0.3.22", "resolved": "https://registry.npmjs.org/typeorm/-/typeorm-0.3.22.tgz", @@ -9776,6 +10264,16 @@ } } }, + "node_modules/typeorm-typedi-extensions": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/typeorm-typedi-extensions/-/typeorm-typedi-extensions-0.4.1.tgz", + "integrity": "sha512-05hWktQ4zuXzTTUO3ao56yOezlvUuZhH2NRS//m0SOGCAJoVlfPTMHcmDaMSQy/lMfAwPWoIyn+sfK7ONzTdXQ==", + "license": "MIT", + "peerDependencies": { + "typedi": ">=0.10.0", + "typeorm": ">=0.2.30" + } + }, "node_modules/typeorm/node_modules/buffer": { "version": "6.0.3", "resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz", @@ -9804,7 +10302,7 @@ "version": "5.8.2", "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.8.2.tgz", "integrity": "sha512-aJn6wq13/afZp/jT9QZmwEjDqqvSGp1VT5GVg+f/t6/oVyrgXM6BY1h9BRh/O5p3PlUPAe+WuiEZOmb/49RqoQ==", - "dev": true, + "devOptional": true, "license": "Apache-2.0", "bin": { "tsc": "bin/tsc", @@ -9830,7 +10328,7 @@ "version": "6.19.8", "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.19.8.tgz", "integrity": "sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw==", - "dev": true, + "devOptional": true, "license": "MIT" }, "node_modules/universalify": { @@ -9925,7 +10423,7 @@ "version": "3.0.1", "resolved": "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz", "integrity": "sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==", - "dev": true, + "devOptional": true, "license": "MIT" }, "node_modules/v8-to-istanbul": { @@ -9943,6 +10441,15 @@ "node": ">=10.12.0" } }, + "node_modules/validator": { + "version": "13.15.0", + "resolved": "https://registry.npmjs.org/validator/-/validator-13.15.0.tgz", + "integrity": "sha512-36B2ryl4+oL5QxZ3AzD0t5SsMNGvTtQHpjgFO5tbNxfXbMFkY822ktCDe1MnlqV3301QQI9SLHDNJokDI+Z9pA==", + "license": "MIT", + "engines": { + "node": ">= 0.10" + } + }, "node_modules/vary": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", @@ -9992,6 +10499,55 @@ "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==", "license": "BSD-2-Clause" }, + "node_modules/webpack": { + "version": "5.99.7", + "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.99.7.tgz", + "integrity": "sha512-CNqKBRMQjwcmKR0idID5va1qlhrqVUKpovi+Ec79ksW8ux7iS1+A6VqzfZXgVYCFRKl7XL5ap3ZoMpwBJxcg0w==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@types/eslint-scope": "^3.7.7", + "@types/estree": "^1.0.6", + "@types/json-schema": "^7.0.15", + "@webassemblyjs/ast": "^1.14.1", + "@webassemblyjs/wasm-edit": "^1.14.1", + "@webassemblyjs/wasm-parser": "^1.14.1", + "acorn": "^8.14.0", + "browserslist": "^4.24.0", + "chrome-trace-event": "^1.0.2", + "enhanced-resolve": "^5.17.1", + "es-module-lexer": "^1.2.1", + "eslint-scope": "5.1.1", + "events": "^3.2.0", + "glob-to-regexp": "^0.4.1", + "graceful-fs": "^4.2.11", + "json-parse-even-better-errors": "^2.3.1", + "loader-runner": "^4.2.0", + "mime-types": "^2.1.27", + "neo-async": "^2.6.2", + "schema-utils": "^4.3.2", + "tapable": "^2.1.1", + "terser-webpack-plugin": "^5.3.11", + "watchpack": "^2.4.1", + "webpack-sources": "^3.2.3" + }, + "bin": { + "webpack": "bin/webpack.js" + }, + "engines": { + "node": ">=10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependenciesMeta": { + "webpack-cli": { + "optional": true + } + } + }, "node_modules/webpack-node-externals": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/webpack-node-externals/-/webpack-node-externals-3.0.0.tgz", @@ -10012,6 +10568,53 @@ "node": ">=10.13.0" } }, + "node_modules/webpack/node_modules/eslint-scope": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", + "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==", + "dev": true, + "license": "BSD-2-Clause", + "peer": true, + "dependencies": { + "esrecurse": "^4.3.0", + "estraverse": "^4.1.1" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/webpack/node_modules/estraverse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", + "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", + "dev": true, + "license": "BSD-2-Clause", + "peer": true, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/webpack/node_modules/schema-utils": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.3.2.tgz", + "integrity": "sha512-Gn/JaSk/Mt9gYubxTtSn/QCV4em9mpAPiR1rqy/Ocu19u/G9J5WWdNoUT4SiV6mFC3y6cxyFcFwdzPM3FgxGAQ==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@types/json-schema": "^7.0.9", + "ajv": "^8.9.0", + "ajv-formats": "^2.1.1", + "ajv-keywords": "^5.1.0" + }, + "engines": { + "node": ">= 10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + } + }, "node_modules/whatwg-url": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", @@ -10037,6 +10640,15 @@ "node": ">= 8" } }, + "node_modules/wide-align": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.5.tgz", + "integrity": "sha512-eDMORYaPNZ4sQIuuYPDHdQvf4gyCF9rEEV/yPxGfwPkRodwEgiMUUXTx/dex+Me0wxx53S+NgUHaP7y3MGlDmg==", + "license": "ISC", + "dependencies": { + "string-width": "^1.0.2 || 2 || 3 || 4" + } + }, "node_modules/winston": { "version": "3.17.0", "resolved": "https://registry.npmjs.org/winston/-/winston-3.17.0.tgz", @@ -10175,7 +10787,6 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", - "dev": true, "license": "ISC" }, "node_modules/write-file-atomic": { @@ -10255,7 +10866,7 @@ "version": "3.1.1", "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz", "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==", - "dev": true, + "devOptional": true, "license": "MIT", "engines": { "node": ">=6" diff --git a/package.json b/package.json index e0d1c33..24911b1 100644 --- a/package.json +++ b/package.json @@ -18,10 +18,11 @@ "test:cov": "jest --coverage", "test:debug": "node --inspect-brk -r tsconfig-paths/register -r ts-node/register node_modules/.bin/jest --runInBand", "test:e2e": "jest --config ./test/jest-e2e.json", - "typeorm": "npm run build && npx typeorm -d dist/database/datasource.js", - "migration:generate": "npm run build && npm run typeorm -- migration:generate src/database/migrations/%npm_config_name%", - "migration:run": "npm run build && npm run typeorm -- migration:run", - "migration:revert": "npm run build && npm run typeorm -- migration:revert" + "typeorm": "ts-node ./node_modules/typeorm/cli", + "migration:run": "npm run typeorm migration:run -- -d ./src/database/datasource.ts", + "migration:generate": "npm run typeorm -- -d ./src/database/datasource.ts migration:generate ./src/database/migrations/$npm_config_name", + "migration:create": "npm run typeorm -- migration:create ./src/database/migrations/$npm_config_name", + "migration:revert": "npm run typeorm -- -d ./src/database/datasource.ts migration:revert" }, "dependencies": { "@nestjs/common": "^10.0.0", @@ -30,15 +31,23 @@ "@nestjs/platform-express": "^10.0.0", "@nestjs/swagger": "^11.1.3", "@nestjs/typeorm": "^11.0.0", + "bcrypt": "^5.1.1", + "bcryptjs": "^3.0.2", + "chance": "^1.1.12", + "class-transformer": "^0.5.1", + "class-validator": "^0.14.1", "compression": "^1.8.0", "dotenv": "^16.5.0", "express-rate-limit": "^7.5.0", "helmet": "^8.1.0", - "pg": "^8.14.1", - "reflect-metadata": "^0.1.13", + "jsonwebtoken": "^9.0.2", + "pg": "^8.15.6", + "reflect-metadata": "^0.1.14", "rxjs": "^7.8.1", "swagger-ui-express": "^5.0.1", + "typedi": "^0.10.0", "typeorm": "^0.3.22", + "typeorm-typedi-extensions": "^0.4.1", "winston": "^3.17.0", "winston-daily-rotate-file": "^5.0.0" }, diff --git a/src/app.controller.ts b/src/app.controller.ts deleted file mode 100644 index cce879e..0000000 --- a/src/app.controller.ts +++ /dev/null @@ -1,12 +0,0 @@ -import { Controller, Get } from '@nestjs/common'; -import { AppService } from './app.service'; - -@Controller() -export class AppController { - constructor(private readonly appService: AppService) {} - - @Get() - getHello(): string { - return this.appService.getHello(); - } -} diff --git a/src/app.module.ts b/src/app.module.ts index 477ab54..37045e8 100644 --- a/src/app.module.ts +++ b/src/app.module.ts @@ -1,23 +1,21 @@ import { Module } from '@nestjs/common'; -import { AppController } from './app.controller'; -import { AppService } from './app.service'; import { ConfigModule } from '@nestjs/config'; -import { config } from './config/config.service'; +import { configs } from './config/config.service'; import { dataSourceOptions } from './database/datasource'; import { TypeOrmModule } from '@nestjs/typeorm'; import { WinstonLoggerService } from './config/logger.service'; - +import { AuthModule } from './auth/auth.module'; @Module({ imports: [ ConfigModule.forRoot({ isGlobal: true, - load: [config] + load: [configs] }), TypeOrmModule.forRoot(dataSourceOptions), - ], - - controllers: [AppController], - providers: [AppService, WinstonLoggerService], + AuthModule, + ], + providers: [ WinstonLoggerService] }) export class AppModule {} + diff --git a/src/app.service.ts b/src/app.service.ts deleted file mode 100644 index 927d7cc..0000000 --- a/src/app.service.ts +++ /dev/null @@ -1,8 +0,0 @@ -import { Injectable } from '@nestjs/common'; - -@Injectable() -export class AppService { - getHello(): string { - return 'Hello World!'; - } -} diff --git a/src/auth/auth.controller.ts b/src/auth/auth.controller.ts new file mode 100644 index 0000000..a40e096 --- /dev/null +++ b/src/auth/auth.controller.ts @@ -0,0 +1,27 @@ +// import { Controller, Post, Body } from '@nestjs/common'; +// import { AuthService } from './auth.service'; + +// @Controller('auth') +// export class AuthController { +// constructor(private readonly authService: AuthService) {} + +// @Post('register') +// async register(@Body() req: { email: string, password: string }): Promise { +// const { email, password } = req; +// return this.authService.registerAdmin(email, password); +// } +// } + +import { Controller, Post, Body } from '@nestjs/common'; +import { AuthService } from './auth.service'; +import { AdminDTO } from './dto/authdto'; // Import the DTO + +@Controller('auth') +export class AuthController { + constructor(private readonly authService: AuthService) {} + + @Post('register') + async register(@Body() req: AdminDTO): Promise { + return this.authService.registerAdmin(req); // Pass the DTO to the service + } +} \ No newline at end of file diff --git a/src/auth/auth.module.ts b/src/auth/auth.module.ts new file mode 100644 index 0000000..cf7baa1 --- /dev/null +++ b/src/auth/auth.module.ts @@ -0,0 +1,20 @@ +import { Module } from '@nestjs/common'; +import { AuthController } from './auth.controller'; +import { TypeOrmModule } from '@nestjs/typeorm'; +import { AuthService } from './auth.service'; +import { Admin } from '../database/entities/admin.entity'; +import { AdminRepository } from 'src/common/repository/adminRepository'; + +@Module({ + imports: [TypeOrmModule.forFeature([Admin])], + controllers: [AuthController], + providers: [ + AuthService, + { + provide: 'AdminRepository', + useClass: AdminRepository, + }, +], +exports: [AuthService], +}) +export class AuthModule {} \ No newline at end of file diff --git a/src/auth/auth.service.ts b/src/auth/auth.service.ts new file mode 100644 index 0000000..1b65f6a --- /dev/null +++ b/src/auth/auth.service.ts @@ -0,0 +1,38 @@ +import { Injectable, ConflictException, InternalServerErrorException } from '@nestjs/common'; +import { Inject } from '@nestjs/common'; +import { InjectRepository } from '@nestjs/typeorm'; +import { AdminRepository } from '../common/repository/adminRepository'; +import { hashString } from '../helpers/utils'; +import { AdminDTO } from './dto/authdto'; +//import { AccountStatus, IAdmin, AdminRole, Admin } from '../database/entities/admin.entity'; + +@Injectable() +export class AuthService { + constructor( + @Inject('AdminRepository') + private readonly adminRepository: AdminRepository, + ) {} + + public async registerAdmin(req: AdminDTO): Promise<{ message: string }> { + const { email, password, name } = req; // Ensure `name` is destructured + + try { + // Check if the admin already exists + const existingAdmin = await this.adminRepository.findOneByEmail(email); + if (existingAdmin) { + throw new ConflictException('Email already exists'); + } + + // Hash the password + const hashedPassword = await hashString(password); + + // Save the new admin to the database + await this.adminRepository.add({ name, email, password: hashedPassword }); + + return { message: 'Registration successful' }; + } catch (error) { + console.error('Error in registerAdmin:', error); + throw new InternalServerErrorException('Failed to register admin'); + } + } +} diff --git a/src/auth/dto/authdto.ts b/src/auth/dto/authdto.ts new file mode 100644 index 0000000..dd0727d --- /dev/null +++ b/src/auth/dto/authdto.ts @@ -0,0 +1,15 @@ +import { IsEmail, IsNotEmpty, IsString } from 'class-validator'; + +export class AdminDTO { + @IsNotEmpty() + @IsString() + name: string; + + @IsNotEmpty() + @IsEmail() + email: string; + + @IsNotEmpty() + @IsString() + password: string; +} \ No newline at end of file diff --git a/src/common/repository/adminRepository.ts b/src/common/repository/adminRepository.ts new file mode 100644 index 0000000..a350099 --- /dev/null +++ b/src/common/repository/adminRepository.ts @@ -0,0 +1,110 @@ +import { Injectable } from '@nestjs/common'; +import { Repository } from 'typeorm'; +import { Admin } from '../../database/entities/admin.entity'; +import dataSource from '../../database/datasource'; +import { v4 as uuidv4 } from 'uuid'; + +@Injectable() +export class AdminRepository { + private readonly entity: Repository; + + constructor() { + this.entity = dataSource.getRepository(Admin); // Use dataSource to get the repository + } + + async findOneByEmail(email: string): Promise { + return await this.entity.findOne({ where: { email } }); + } + + async add(adminData: Partial): Promise { + const admin = this.entity.create({ + ...adminData, + admin_id: uuidv4(), // Generate a unique ID for admin_id + }); + return await this.entity.save(admin); + } + + // async findById(id: string): Promise { + // return await this.entity.findOne({ where: { admin_id: id } }); + // } + + // async updateById(id: string, updates: Partial): Promise { + // const admin = await this.findById(id); + // if (!admin) return null; + + // await this.entity.update({ admin_id: id }, updates); + // return { ...admin, ...updates } as Admin; + // } +} + + + + + + + +// import { +// Repository, +// DeepPartial, +// FindOptionsWhere, +// FindOptionsRelations, +// DeleteResult, +// FindManyOptions, +// SelectQueryBuilder, +// } from 'typeorm'; +// import { Injectable, NotFoundException } from '@nestjs/common'; + +// @Injectable() +// export class BaseRepository { +// [x: string]: any; +// constructor(private readonly entity: Repository) {} + +// async create(createdEntityData: DeepPartial): Promise { +// const createdEntity = this.entity.create(createdEntityData); +// return await this.entity.save(createdEntity); +// } + +// async findOne( +// filterData: FindOptionsWhere, +// relations?: FindOptionsRelations, +// ): Promise { +// return await this.entity.findOne({ +// where: filterData, +// relations: relations, +// }); +// } + +// async findOneAndUpdate( +// filterData: FindOptionsWhere, +// updateData: Partial, +// ): Promise { +// const existingEntity = await this.findOne(filterData); +// if (!existingEntity) { +// throw new NotFoundException(`Data for ${filterData} not found`); +// } +// await this.entity.update(filterData, updateData); +// return this.entity.findOne({ where: filterData }); +// } + +// async delete(filterData: FindOptionsWhere): Promise { +// const result: DeleteResult = await this.entity.delete(filterData); +// return result.affected! > 0; +// } + +// async findAll(): Promise { +// return await this.entity.find(); +// } + +// async find( +// filterData: FindOptionsWhere | FindManyOptions, +// ): Promise { +// return this.entity.find(filterData); +// } + +// async createQueryBuilder( +// alias?: string, +// ): Promise> { +// return this.entity.createQueryBuilder(alias); +// } +// } + \ No newline at end of file diff --git a/src/common/repository/postgres.repository.ts b/src/common/repository/postgres.repository.ts deleted file mode 100644 index d337e0c..0000000 --- a/src/common/repository/postgres.repository.ts +++ /dev/null @@ -1,65 +0,0 @@ -import { - Repository, - DeepPartial, - FindOptionsWhere, - FindOptionsRelations, - DeleteResult, - FindManyOptions, - SelectQueryBuilder, - } from 'typeorm'; - import { Injectable, NotFoundException } from '@nestjs/common'; - - @Injectable() - export class BaseRepository { - [x: string]: any; - constructor(private readonly entity: Repository) {} - - async create(createdEntityData: DeepPartial): Promise { - const createdEntity = this.entity.create(createdEntityData); - return await this.entity.save(createdEntity); - } - - async findOne( - filterData: FindOptionsWhere, - relations?: FindOptionsRelations, - ): Promise { - return await this.entity.findOne({ - where: filterData, - relations: relations, - }); - } - - async findOneAndUpdate( - filterData: FindOptionsWhere, - updateData: Partial, - ): Promise { - const existingEntity = await this.findOne(filterData); - if (!existingEntity) { - throw new NotFoundException(`Data for ${filterData} not found`); - } - await this.entity.update(filterData, updateData); - return this.entity.findOne({ where: filterData }); - } - - async delete(filterData: FindOptionsWhere): Promise { - const result: DeleteResult = await this.entity.delete(filterData); - return result.affected! > 0; - } - - async findAll(): Promise { - return await this.entity.find(); - } - - async find( - filterData: FindOptionsWhere | FindManyOptions, - ): Promise { - return this.entity.find(filterData); - } - - async createQueryBuilder( - alias?: string, - ): Promise> { - return this.entity.createQueryBuilder(alias); - } - } - \ No newline at end of file diff --git a/src/config/config.service.ts b/src/config/config.service.ts index 98940ff..f551ee2 100644 --- a/src/config/config.service.ts +++ b/src/config/config.service.ts @@ -1,19 +1,29 @@ import * as dotenv from 'dotenv'; dotenv.config(); +import { Admin } from '../database/entities/admin.entity'; +import { Migration } from 'typeorm'; -export const config = () => ({ +export const configs = () => ({ PORT: { APP_PORT: process.env.PORT, }, - + JWT_TOKEN: { + JWT_SECRET: process.env.JWT_SECRET, + // REFRESH_JWT_SECRET: process.env.REFRESH_JWT_SECRET, + JWT_ISSUER: process.env.JWT_ISSUER + }, database: { type: 'postgres', host: process.env.PG_HOST, port: parseInt(process.env.PG_PORT ), username:process.env.PG_USERNAME, password:process.env.PG_PASSWORD, - database:process.env.PG_DATABASE - //url: process.env.DB_URL, + database:process.env.PG_DATABASE, + // entities: process.env.NODE_ENV === 'production' ? ["dist/**/*.entity{.ts,.js}"] : ['./database/entities/*.ts'], + entities: [Admin], + migrations: ['./src/database/migrations/*.ts'] + //migrations: process.env.NODE_ENV === 'production' ? ["dist/migrations/*{.ts,.js}"] : ['./database/migrations/*.ts'], + //url: process.env.DB_URL, }, log: { diff --git a/src/config/logger.service.ts b/src/config/logger.service.ts index 168e31e..1d06f06 100644 --- a/src/config/logger.service.ts +++ b/src/config/logger.service.ts @@ -2,7 +2,7 @@ import { Injectable, LoggerService } from '@nestjs/common'; import * as winston from 'winston'; import * as DailyRotateFile from 'winston-daily-rotate-file'; import { hostname } from 'os'; -import { config } from './config.service'; +import { configs } from './config.service'; const { combine, timestamp, printf, errors, colorize } = winston.format; @@ -11,7 +11,7 @@ const { combine, timestamp, printf, errors, colorize } = winston.format; export class WinstonLoggerService implements LoggerService { private logger = winston.createLogger({ levels: winston.config.syslog.levels, - level: config().log.level || 'info', // Add fallback for log level + level: configs().log.level || 'info', // Add fallback for log level format: combine( errors({ stack: true }), timestamp({ format: 'YYYY-MM-DD HH:mm:ss' }), @@ -26,7 +26,7 @@ export class WinstonLoggerService implements LoggerService { dirname: `logs/${hostname()}/combined`, filename: 'combined', extension: '.log', - level: config().isProduction ? 'info' : 'debug', + level: configs().isProduction ? 'info' : 'debug', }), new DailyRotateFile({ dirname: `logs/${hostname()}/error`, diff --git a/src/database/datasource.ts b/src/database/datasource.ts index a63c1c1..aa2b6f6 100644 --- a/src/database/datasource.ts +++ b/src/database/datasource.ts @@ -1,21 +1,25 @@ -import { config } from "src/config/config.service"; +import { configs } from "../config/config.service"; import { DataSource, DataSourceOptions } from "typeorm"; +import { Admin } from "./entities/admin.entity"; export const dataSourceOptions: DataSourceOptions ={ type: "postgres", - host: config().database.host, - port: config().database.port, - username: config().database.username, - password: config().database.password, - database: config().database.database, - entities: ['dist/**/*.entity.js'], - migrations: ['dist/database/migrations/*.js'], + host: configs().database.host, + port: configs().database.port, + username: configs().database.username, + password: configs().database.password, + database: configs().database.database, + entities: configs().database.entities, + migrations: configs().database.migrations, + // entities: ["dist/**/*.entity{.ts,.js}"], + // migrations: ["dist/migrations/*{.ts,.js}"], synchronize: false, logging: true, - ssl: { - rejectUnauthorized: false, // Example: Useful in some environments like Heroku - }, + // ssl: { + // rejectUnauthorized: false, // Example: Useful in some environments like Heroku + // }, + ssl: process.env.isProduction ? { rejectUnauthorized: false } : false }; const dataSource = new DataSource(dataSourceOptions); diff --git a/src/database/entities/admin-notification.entity.ts b/src/database/entities/admin-notification.entity.ts index a683fd6..8aa4d64 100644 --- a/src/database/entities/admin-notification.entity.ts +++ b/src/database/entities/admin-notification.entity.ts @@ -1,24 +1,24 @@ -import { Entity, PrimaryGeneratedColumn, Column, ManyToOne, JoinColumn, CreateDateColumn } from 'typeorm'; -import { Admin } from './admin.entity'; +// import { Entity, PrimaryGeneratedColumn, Column, ManyToOne, JoinColumn, CreateDateColumn } from 'typeorm'; +// import { Admin } from './admin.entity'; -@Entity('admin_notifications') -export class AdminNotification { - @PrimaryGeneratedColumn('uuid') - id: string; +// @Entity('admin_notifications') +// export class AdminNotification { +// @PrimaryGeneratedColumn('uuid') +// id: string; - @ManyToOne(() => Admin) - @JoinColumn({ name: 'admin_id' }) - admin: Admin; +// @ManyToOne(() => Admin) +// @JoinColumn({ name: 'admin_id' }) +// admin: Admin; - @Column() - title: string; +// @Column() +// title: string; - @Column() - message: string; +// @Column() +// message: string; - @Column({ default: false }) - read: boolean; +// @Column({ default: false }) +// read: boolean; - @CreateDateColumn() - createdAt: Date; -} +// @CreateDateColumn() +// createdAt: Date; +// } diff --git a/src/database/entities/admin.entity.ts b/src/database/entities/admin.entity.ts index 6fe51ee..ca065a5 100644 --- a/src/database/entities/admin.entity.ts +++ b/src/database/entities/admin.entity.ts @@ -1,30 +1,42 @@ -import { Entity, PrimaryGeneratedColumn, Column, ManyToOne, JoinColumn, CreateDateColumn, UpdateDateColumn } from 'typeorm'; -import { Role } from './role.entity'; +import { Column , PrimaryColumn, CreateDateColumn, Entity, UpdateDateColumn } from "typeorm"; +import { BaseModel } from "../../database/entities/base.entity"; -@Entity('admins') -export class Admin { - @PrimaryGeneratedColumn('uuid') - id: string; +export enum AccountStatus { + ACTIVE = "active", + SUSPENDED = "suspended", + BANNED = "banned" +} - @Column() - name: string; +export enum AdminRole { + SUPER_ADMIN = "super_admin", + ADMIN = "admin", + MODERATOR = "moderator", +} - @Column({ unique: true }) +export interface IAdmin{ + _id: string; email: string; + password: string; + status: AccountStatus; +} - @Column() - passwordHash: string; +@Entity('admins') +export class Admin { + @PrimaryColumn() + admin_id: string; - @ManyToOne(() => Role) - @JoinColumn({ name: 'role_id' }) - role: Role; + @Column({ nullable: false }) + name: string; - @Column({ default: 'active' }) - status: 'active' | 'suspended'; + @Column({ nullable: false, unique: true }) + email: string; - @CreateDateColumn() - createdAt: Date; + @Column({ nullable: false }) + password: string; - @UpdateDateColumn() - updatedAt: Date; -} + @Column({ type: 'enum', enum: ['active', 'inactive'], default: 'active' }) + status: string; + + @Column({ type: 'enum', enum: ['admin', 'super_admin'], default: 'admin' }) + role: string; +} \ No newline at end of file diff --git a/src/database/entities/audit-log.entity.ts b/src/database/entities/audit-log.entity.ts index 8b87ab5..f1e8d78 100644 --- a/src/database/entities/audit-log.entity.ts +++ b/src/database/entities/audit-log.entity.ts @@ -1,27 +1,27 @@ -import { Entity, PrimaryGeneratedColumn, Column, ManyToOne, JoinColumn, CreateDateColumn } from 'typeorm'; -import { Admin } from './admin.entity'; +// import { Entity, PrimaryGeneratedColumn, Column, ManyToOne, JoinColumn, CreateDateColumn } from 'typeorm'; +// import { Admin } from './admin.entity'; -@Entity('audit_logs') -export class AuditLog { - @PrimaryGeneratedColumn('uuid') - id: string; +// @Entity('audit_logs') +// export class AuditLog { +// @PrimaryGeneratedColumn('uuid') +// id: string; - @ManyToOne(() => Admin) - @JoinColumn({ name: 'admin_id' }) - admin: Admin; +// @ManyToOne(() => Admin) +// @JoinColumn({ name: 'admin_id' }) +// admin: Admin; - @Column() - action: string; +// @Column() +// action: string; - @Column() - targetType: string; // 'user', 'account', etc. +// @Column() +// targetType: string; // 'user', 'account', etc. - @Column() - targetId: string; +// @Column() +// targetId: string; - @Column('jsonb', { nullable: true }) - meta: Record; +// @Column('jsonb', { nullable: true }) +// meta: Record; - @CreateDateColumn() - timestamp: Date; -} +// @CreateDateColumn() +// timestamp: Date; +// } diff --git a/src/database/entities/base.entity.ts b/src/database/entities/base.entity.ts new file mode 100644 index 0000000..dfb02d3 --- /dev/null +++ b/src/database/entities/base.entity.ts @@ -0,0 +1,20 @@ +import { + CreateDateColumn, + DeleteDateColumn, + PrimaryGeneratedColumn, + UpdateDateColumn +} from 'typeorm'; + +export abstract class BaseModel { + @PrimaryGeneratedColumn('increment') + id!: number; + + @CreateDateColumn() + created_at!: Date; + + @UpdateDateColumn({ nullable: true }) + updated_at?: Date; + + @DeleteDateColumn({ nullable: true }) + deleted_at?: Date; +} \ No newline at end of file diff --git a/src/database/entities/role.entity.ts b/src/database/entities/role.entity.ts index 209f773..5bbcbfc 100644 --- a/src/database/entities/role.entity.ts +++ b/src/database/entities/role.entity.ts @@ -1,19 +1,19 @@ -import { Entity, PrimaryGeneratedColumn, Column, CreateDateColumn, UpdateDateColumn } from 'typeorm'; +// import { Entity, PrimaryGeneratedColumn, Column, CreateDateColumn, UpdateDateColumn } from 'typeorm'; -@Entity('roles') -export class Role { - @PrimaryGeneratedColumn('uuid') - id: string; +// @Entity('roles') +// export class Role { +// @PrimaryGeneratedColumn('uuid') +// id: string; - @Column({ unique: true }) - name: string; +// @Column({ unique: true }) +// name: string; - @Column('simple-array') // Or use a separate permission entity if complex - permissions: string[]; +// @Column('simple-array') // Or use a separate permission entity if complex +// permissions: string[]; - @CreateDateColumn() - createdAt: Date; +// @CreateDateColumn() +// createdAt: Date; - @UpdateDateColumn() - updatedAt: Date; -} +// @UpdateDateColumn() +// updatedAt: Date; +// } diff --git a/src/database/migrations/1745272202322-createAdminServiceTables.ts b/src/database/migrations/1745272202322-createAdminServiceTables.ts deleted file mode 100644 index 050171e..0000000 --- a/src/database/migrations/1745272202322-createAdminServiceTables.ts +++ /dev/null @@ -1,26 +0,0 @@ -import { MigrationInterface, QueryRunner } from "typeorm"; - -export class CreateAdminServiceTables1745272202322 implements MigrationInterface { - name = 'CreateAdminServiceTables1745272202322' - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`CREATE TABLE "roles" ("id" uuid NOT NULL DEFAULT uuid_generate_v4(), "name" character varying NOT NULL, "permissions" text NOT NULL, "createdAt" TIMESTAMP NOT NULL DEFAULT now(), "updatedAt" TIMESTAMP NOT NULL DEFAULT now(), CONSTRAINT "UQ_648e3f5447f725579d7d4ffdfb7" UNIQUE ("name"), CONSTRAINT "PK_c1433d71a4838793a49dcad46ab" PRIMARY KEY ("id"))`); - await queryRunner.query(`CREATE TABLE "admins" ("id" uuid NOT NULL DEFAULT uuid_generate_v4(), "name" character varying NOT NULL, "email" character varying NOT NULL, "passwordHash" character varying NOT NULL, "status" character varying NOT NULL DEFAULT 'active', "createdAt" TIMESTAMP NOT NULL DEFAULT now(), "updatedAt" TIMESTAMP NOT NULL DEFAULT now(), "role_id" uuid, CONSTRAINT "UQ_051db7d37d478a69a7432df1479" UNIQUE ("email"), CONSTRAINT "PK_e3b38270c97a854c48d2e80874e" PRIMARY KEY ("id"))`); - await queryRunner.query(`CREATE TABLE "admin_notifications" ("id" uuid NOT NULL DEFAULT uuid_generate_v4(), "title" character varying NOT NULL, "message" character varying NOT NULL, "read" boolean NOT NULL DEFAULT false, "createdAt" TIMESTAMP NOT NULL DEFAULT now(), "admin_id" uuid, CONSTRAINT "PK_1fecd1cab747b7ab6e850091901" PRIMARY KEY ("id"))`); - await queryRunner.query(`CREATE TABLE "audit_logs" ("id" uuid NOT NULL DEFAULT uuid_generate_v4(), "action" character varying NOT NULL, "targetType" character varying NOT NULL, "targetId" character varying NOT NULL, "meta" jsonb, "timestamp" TIMESTAMP NOT NULL DEFAULT now(), "admin_id" uuid, CONSTRAINT "PK_1bb179d048bbc581caa3b013439" PRIMARY KEY ("id"))`); - await queryRunner.query(`ALTER TABLE "admins" ADD CONSTRAINT "FK_5733c73cd81c566a90cc4802f96" FOREIGN KEY ("role_id") REFERENCES "roles"("id") ON DELETE NO ACTION ON UPDATE NO ACTION`); - await queryRunner.query(`ALTER TABLE "admin_notifications" ADD CONSTRAINT "FK_c07cffb34f4e05a78f7663bc611" FOREIGN KEY ("admin_id") REFERENCES "admins"("id") ON DELETE NO ACTION ON UPDATE NO ACTION`); - await queryRunner.query(`ALTER TABLE "audit_logs" ADD CONSTRAINT "FK_b29de603374cbfa7d776d88e5b5" FOREIGN KEY ("admin_id") REFERENCES "admins"("id") ON DELETE NO ACTION ON UPDATE NO ACTION`); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "audit_logs" DROP CONSTRAINT "FK_b29de603374cbfa7d776d88e5b5"`); - await queryRunner.query(`ALTER TABLE "admin_notifications" DROP CONSTRAINT "FK_c07cffb34f4e05a78f7663bc611"`); - await queryRunner.query(`ALTER TABLE "admins" DROP CONSTRAINT "FK_5733c73cd81c566a90cc4802f96"`); - await queryRunner.query(`DROP TABLE "audit_logs"`); - await queryRunner.query(`DROP TABLE "admin_notifications"`); - await queryRunner.query(`DROP TABLE "admins"`); - await queryRunner.query(`DROP TABLE "roles"`); - } - -} diff --git a/src/helpers/utils.ts b/src/helpers/utils.ts new file mode 100644 index 0000000..dc20938 --- /dev/null +++ b/src/helpers/utils.ts @@ -0,0 +1,71 @@ +//import { CharacterCasing } from "../constants/enums"; +import Chance from "chance"; +//import bcrypt from "bcryptjs"; +const bcrypt = require("bcrypt"); +import jwt from "jsonwebtoken" +import { configs } from "../config/config.service"; +//import { uuid } from "uuidv4"; + + +// const chance = new Chance(); +// export function generateRandomString( +// { +// length = CONFIGS.DEFAULT_CHARACTER_LENGTH, +// casing = CharacterCasing.LOWER, +// numericOnly = false +// }: +// { length?: number, casing?: "upper" | "lower", numericOnly?: boolean } +// ): string { +// if (length <= 0) return ""; + +// const randomString = chance.string({ length, casing, alpha: !numericOnly, numeric: true }); +// return randomString; +// } + + export async function hashString(input: string): Promise < string > { + if(!input) return ""; + + const salt = await bcrypt.genSalt(10); + const hashedString = await bcrypt.hash(input, salt); + + return hashedString; +} + + export async function compareHash(input: string, hash: string): Promise < boolean > { + const isSame = await bcrypt.compare(input, hash); + return isSame; +} + + /** + * generateJWT + */ + export function generateJWT(email: string, user_id: string) { + const jwtData = { + email, + user_id + } + return jwt.sign({ jwtData }, configs().JWT_TOKEN.JWT_SECRET, { + expiresIn: '3600s', + issuer: configs().JWT_TOKEN.JWT_ISSUER, + audience: email + }); + } + + // export function generateUUID() { + // const currentTime = moment(); + // const lifeSpan = 24 + // const expiresAt = currentTime.add(lifeSpan,'hours').format() as unknown as Date; + // return { + // uuid: uuid(), + // expiresAt + // } + // } + + + // export function hasExpired(expectedExpirationDate: any){ + // const currentTime = moment(); + // if(currentTime.isAfter(expectedExpirationDate)){ + // return true; + // } + // return false + // } \ No newline at end of file diff --git a/src/ioc.ts b/src/ioc.ts new file mode 100644 index 0000000..530adfe --- /dev/null +++ b/src/ioc.ts @@ -0,0 +1,7 @@ +import { Container, Service } from "typedi"; + +export const iocContainer = { + get(someClass: { new(...args: any[]): T }): T { + return Container.get(someClass); + } +}; diff --git a/src/main.ts b/src/main.ts index 8437bf9..bab09c7 100644 --- a/src/main.ts +++ b/src/main.ts @@ -1,13 +1,14 @@ import { NestFactory } from '@nestjs/core'; +import { ValidationPipe } from '@nestjs/common'; import { AppModule } from './app.module'; import { postgresLoader } from './database/datasource'; import { WinstonLoggerService } from './config/logger.service'; -import { config } from './config/config.service'; +import { configs } from './config/config.service'; import { DocumentBuilder, SwaggerModule } from '@nestjs/swagger'; import { CustomErrorFilter } from './helpers/errorHandler'; async function bootstrap() { - const appConfig = config() + const appConfig = configs() //initialize postgres connection await postgresLoader(); @@ -16,6 +17,7 @@ async function bootstrap() { // Register global exception filter app.useGlobalFilters(new CustomErrorFilter()) + app.useGlobalPipes(new ValidationPipe()); //set up custom logger const logger = app.get(WinstonLoggerService); // Get the WinstonLoggerService instance @@ -35,6 +37,6 @@ async function bootstrap() { //start the application const port = appConfig.PORT.APP_PORT await app.listen(port); - console.log(`Application is running on: http://localhost:${port}`); + logger.log(`Application is running on: http://localhost:${port}`); } bootstrap(); diff --git a/tsconfig.json b/tsconfig.json index 95f5641..cb76504 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -16,6 +16,7 @@ "noImplicitAny": false, "strictBindCallApply": false, "forceConsistentCasingInFileNames": false, - "noFallthroughCasesInSwitch": false + "noFallthroughCasesInSwitch": false, + } } From 3be657d7245845e1b1594c539c5a3cee58ab2923 Mon Sep 17 00:00:00 2001 From: "Salawu O. Joseph" Date: Wed, 30 Apr 2025 14:03:08 +0100 Subject: [PATCH 2/2] Login implemented --- .env.example | 7 -- env.txt | 13 +++ package-lock.json | 107 ++++++++++++++++++++++- package.json | 6 ++ src/auth/auth.controller.ts | 34 ++++--- src/auth/auth.module.ts | 11 ++- src/auth/auth.service.ts | 34 ++++++- src/auth/dto/authdto.ts | 28 +++++- src/common/repository/adminRepository.ts | 4 +- src/database/datasource.ts | 2 +- src/database/entities/admin.entity.ts | 2 +- src/helpers/utils.ts | 25 +++--- 12 files changed, 223 insertions(+), 50 deletions(-) delete mode 100644 .env.example create mode 100644 env.txt diff --git a/.env.example b/.env.example deleted file mode 100644 index bd4d0a9..0000000 --- a/.env.example +++ /dev/null @@ -1,7 +0,0 @@ -PG_USERNAME= -PG_PASSWORD= -PG_HOST= -PG_PORT= -PG_DATBASE= -LOG_LEVEL=debug # Or 'info', 'warn', etc. -NODE_ENV=development \ No newline at end of file diff --git a/env.txt b/env.txt new file mode 100644 index 0000000..ef1888a --- /dev/null +++ b/env.txt @@ -0,0 +1,13 @@ +PG_USERNAME=postgres +PG_PASSWORD=masterjoe +PG_HOST=localhost +PG_PORT=5432 +PG_DATBASE=authService +LOG_LEVEL=debug # Or 'info', 'warn', etc. +NODE_ENV=development + +PORT=3000 + +JWT_SECRET=masterjoe +# REFRESH_JWT_SECRET=awesome +JWT_ISSUER=masterjoe \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index 2d68f43..e6be56e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -12,6 +12,8 @@ "@nestjs/common": "^10.0.0", "@nestjs/config": "^4.0.2", "@nestjs/core": "^10.0.0", + "@nestjs/jwt": "^11.0.0", + "@nestjs/passport": "^11.0.5", "@nestjs/platform-express": "^10.0.0", "@nestjs/swagger": "^7.3.1", "@nestjs/typeorm": "^11.0.0", @@ -25,6 +27,9 @@ "express-rate-limit": "^7.5.0", "helmet": "^8.1.0", "jsonwebtoken": "^9.0.2", + "moment": "^2.30.1", + "passport": "^0.7.0", + "passport-jwt": "^4.0.1", "pg": "^8.15.6", "reflect-metadata": "^0.1.14", "rxjs": "^7.8.1", @@ -32,6 +37,7 @@ "typedi": "^0.10.0", "typeorm": "^0.3.22", "typeorm-typedi-extensions": "^0.4.1", + "uuidv4": "^6.2.13", "winston": "^3.17.0", "winston-daily-rotate-file": "^5.0.0" }, @@ -1884,6 +1890,19 @@ } } }, + "node_modules/@nestjs/jwt": { + "version": "11.0.0", + "resolved": "https://registry.npmjs.org/@nestjs/jwt/-/jwt-11.0.0.tgz", + "integrity": "sha512-v7YRsW3Xi8HNTsO+jeHSEEqelX37TVWgwt+BcxtkG/OfXJEOs6GZdbdza200d6KqId1pJQZ6UPj1F0M6E+mxaA==", + "license": "MIT", + "dependencies": { + "@types/jsonwebtoken": "9.0.7", + "jsonwebtoken": "9.0.2" + }, + "peerDependencies": { + "@nestjs/common": "^8.0.0 || ^9.0.0 || ^10.0.0 || ^11.0.0" + } + }, "node_modules/@nestjs/mapped-types": { "version": "2.0.5", "resolved": "https://registry.npmjs.org/@nestjs/mapped-types/-/mapped-types-2.0.5.tgz", @@ -1904,6 +1923,16 @@ } } }, + "node_modules/@nestjs/passport": { + "version": "11.0.5", + "resolved": "https://registry.npmjs.org/@nestjs/passport/-/passport-11.0.5.tgz", + "integrity": "sha512-ulQX6mbjlws92PIM15Naes4F4p2JoxGnIJuUsdXQPT+Oo2sqQmENEZXM7eYuimocfHnKlcfZOuyzbA33LwUlOQ==", + "license": "MIT", + "peerDependencies": { + "@nestjs/common": "^10.0.0 || ^11.0.0", + "passport": "^0.5.0 || ^0.6.0 || ^0.7.0" + } + }, "node_modules/@nestjs/platform-express": { "version": "10.4.15", "resolved": "https://registry.npmjs.org/@nestjs/platform-express/-/platform-express-10.4.15.tgz", @@ -2420,6 +2449,15 @@ "dev": true, "license": "MIT" }, + "node_modules/@types/jsonwebtoken": { + "version": "9.0.7", + "resolved": "https://registry.npmjs.org/@types/jsonwebtoken/-/jsonwebtoken-9.0.7.tgz", + "integrity": "sha512-ugo316mmTYBl2g81zDFnZ7cfxlut3o+/EQdaP7J8QN2kY6lJ22hmQYCK5EHcJHbrW+dkCGSCPgbG8JtYj6qSrg==", + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, "node_modules/@types/methods": { "version": "1.1.4", "resolved": "https://registry.npmjs.org/@types/methods/-/methods-1.1.4.tgz", @@ -2438,7 +2476,6 @@ "version": "20.17.30", "resolved": "https://registry.npmjs.org/@types/node/-/node-20.17.30.tgz", "integrity": "sha512-7zf4YyHA+jvBNfVrk2Gtvs6x7E8V+YDW05bNfG2XkWDJfYRXrTiP/DsB2zSYTaHX0bGIujTBQdMVAhb+j7mwpg==", - "devOptional": true, "license": "MIT", "dependencies": { "undici-types": "~6.19.2" @@ -2524,6 +2561,12 @@ "integrity": "sha512-6WaYesThRMCl19iryMYP7/x2OVgCtbIVflDGFpWnb9irXI3UjYE4AzmYuiUKY1AJstGijoY+MgUszMgRxIYTYw==", "license": "MIT" }, + "node_modules/@types/uuid": { + "version": "8.3.4", + "resolved": "https://registry.npmjs.org/@types/uuid/-/uuid-8.3.4.tgz", + "integrity": "sha512-c/I8ZRb51j+pYGAu5CrFMRxqZ2ke4y2grEBO5AUjgSkSk+qT2Ea+OdWElz/OiMf5MNpn2b17kuVBwZLQJXzihw==", + "license": "MIT" + }, "node_modules/@types/validator": { "version": "13.15.0", "resolved": "https://registry.npmjs.org/@types/validator/-/validator-13.15.0.tgz", @@ -8141,6 +8184,42 @@ "node": ">= 0.8" } }, + "node_modules/passport": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/passport/-/passport-0.7.0.tgz", + "integrity": "sha512-cPLl+qZpSc+ireUvt+IzqbED1cHHkDoVYMo30jbJIdOOjQ1MQYZBPiNvmi8UM6lJuOpTPXJGZQk0DtC4y61MYQ==", + "license": "MIT", + "dependencies": { + "passport-strategy": "1.x.x", + "pause": "0.0.1", + "utils-merge": "^1.0.1" + }, + "engines": { + "node": ">= 0.4.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/jaredhanson" + } + }, + "node_modules/passport-jwt": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/passport-jwt/-/passport-jwt-4.0.1.tgz", + "integrity": "sha512-UCKMDYhNuGOBE9/9Ycuoyh7vP6jpeTp/+sfMJl7nLff/t6dps+iaeE0hhNkKN8/HZHcJ7lCdOyDxHdDoxoSvdQ==", + "license": "MIT", + "dependencies": { + "jsonwebtoken": "^9.0.0", + "passport-strategy": "^1.0.0" + } + }, + "node_modules/passport-strategy": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/passport-strategy/-/passport-strategy-1.0.0.tgz", + "integrity": "sha512-CB97UUvDKJde2V0KDWWB3lyf6PC3FaZP7YxZ2G8OAtn9p4HI9j9JLP9qjOGZFvyl8uwNT8qM+hGnz/n16NI7oA==", + "engines": { + "node": ">= 0.4.0" + } + }, "node_modules/path-exists": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", @@ -8214,6 +8293,11 @@ "node": ">=8" } }, + "node_modules/pause": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/pause/-/pause-0.0.1.tgz", + "integrity": "sha512-KG8UEiEVkR3wGEb4m5yZkVCzigAD+cVEJck2CzYZO37ZGJfctvVptVO192MwrtPhzONn6go8ylnOdMhKqi4nfg==" + }, "node_modules/peek-readable": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/peek-readable/-/peek-readable-7.0.0.tgz", @@ -10433,7 +10517,6 @@ "version": "6.19.8", "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.19.8.tgz", "integrity": "sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw==", - "devOptional": true, "license": "MIT" }, "node_modules/universalify": { @@ -10524,6 +10607,26 @@ "uuid": "dist/esm/bin/uuid" } }, + "node_modules/uuidv4": { + "version": "6.2.13", + "resolved": "https://registry.npmjs.org/uuidv4/-/uuidv4-6.2.13.tgz", + "integrity": "sha512-AXyzMjazYB3ovL3q051VLH06Ixj//Knx7QnUSi1T//Ie3io6CpsPu9nVMOx5MoLWh6xV0B9J0hIaxungxXUbPQ==", + "deprecated": "Package no longer supported. Contact Support at https://www.npmjs.com/support for more info.", + "license": "MIT", + "dependencies": { + "@types/uuid": "8.3.4", + "uuid": "8.3.2" + } + }, + "node_modules/uuidv4/node_modules/uuid": { + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", + "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", + "license": "MIT", + "bin": { + "uuid": "dist/bin/uuid" + } + }, "node_modules/v8-compile-cache-lib": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz", diff --git a/package.json b/package.json index 5f5ca67..7571128 100644 --- a/package.json +++ b/package.json @@ -28,6 +28,8 @@ "@nestjs/common": "^10.0.0", "@nestjs/config": "^4.0.2", "@nestjs/core": "^10.0.0", + "@nestjs/jwt": "^11.0.0", + "@nestjs/passport": "^11.0.5", "@nestjs/platform-express": "^10.0.0", "@nestjs/swagger": "^7.3.1", "@nestjs/typeorm": "^11.0.0", @@ -41,6 +43,9 @@ "express-rate-limit": "^7.5.0", "helmet": "^8.1.0", "jsonwebtoken": "^9.0.2", + "moment": "^2.30.1", + "passport": "^0.7.0", + "passport-jwt": "^4.0.1", "pg": "^8.15.6", "reflect-metadata": "^0.1.14", "rxjs": "^7.8.1", @@ -48,6 +53,7 @@ "typedi": "^0.10.0", "typeorm": "^0.3.22", "typeorm-typedi-extensions": "^0.4.1", + "uuidv4": "^6.2.13", "winston": "^3.17.0", "winston-daily-rotate-file": "^5.0.0" }, diff --git a/src/auth/auth.controller.ts b/src/auth/auth.controller.ts index a40e096..3a9c38d 100644 --- a/src/auth/auth.controller.ts +++ b/src/auth/auth.controller.ts @@ -1,27 +1,25 @@ -// import { Controller, Post, Body } from '@nestjs/common'; -// import { AuthService } from './auth.service'; - -// @Controller('auth') -// export class AuthController { -// constructor(private readonly authService: AuthService) {} - -// @Post('register') -// async register(@Body() req: { email: string, password: string }): Promise { -// const { email, password } = req; -// return this.authService.registerAdmin(email, password); -// } -// } - -import { Controller, Post, Body } from '@nestjs/common'; +import { Controller, Post, Body, Res, HttpStatus } from '@nestjs/common'; import { AuthService } from './auth.service'; -import { AdminDTO } from './dto/authdto'; // Import the DTO - +import { AdminDTO, AuthAdminDTO } from './dto/authdto'; // Import the DTO +import { Response } from 'express'; @Controller('auth') export class AuthController { constructor(private readonly authService: AuthService) {} @Post('register') async register(@Body() req: AdminDTO): Promise { - return this.authService.registerAdmin(req); // Pass the DTO to the service + return this.authService.registerAdmin(req); + } + + @Post('login') + async login(@Body() req: AuthAdminDTO, @Res() res: Response): Promise { + const { token, message } = await this.authService.loginAdmin(req); + + // Set the token in the response headers + res.setHeader('Authorization', `Bearer ${token}`); + return res.status(HttpStatus.OK).json({ + isSuccess: true, + message, + }); } } \ No newline at end of file diff --git a/src/auth/auth.module.ts b/src/auth/auth.module.ts index cf7baa1..0998385 100644 --- a/src/auth/auth.module.ts +++ b/src/auth/auth.module.ts @@ -1,4 +1,6 @@ import { Module } from '@nestjs/common'; +import { JwtModule } from '@nestjs/jwt'; +import { PassportModule } from '@nestjs/passport'; import { AuthController } from './auth.controller'; import { TypeOrmModule } from '@nestjs/typeorm'; import { AuthService } from './auth.service'; @@ -6,7 +8,14 @@ import { Admin } from '../database/entities/admin.entity'; import { AdminRepository } from 'src/common/repository/adminRepository'; @Module({ - imports: [TypeOrmModule.forFeature([Admin])], + imports: [ + TypeOrmModule.forFeature([Admin]), + PassportModule, + JwtModule.register({ + secret: process.env.JWT_SECRET || 'your_jwt_secret', // Replace with your secret + signOptions: { expiresIn: '1h' }, // Token expiration time + }) + ], controllers: [AuthController], providers: [ AuthService, diff --git a/src/auth/auth.service.ts b/src/auth/auth.service.ts index 1b65f6a..9f453f8 100644 --- a/src/auth/auth.service.ts +++ b/src/auth/auth.service.ts @@ -2,15 +2,19 @@ import { Injectable, ConflictException, InternalServerErrorException } from '@ne import { Inject } from '@nestjs/common'; import { InjectRepository } from '@nestjs/typeorm'; import { AdminRepository } from '../common/repository/adminRepository'; -import { hashString } from '../helpers/utils'; -import { AdminDTO } from './dto/authdto'; +import { compareHash, generateJWT, hashString } from '../helpers/utils'; +import { AdminDTO, AuthAdminDTO } from './dto/authdto'; +import { IAdmin } from 'src/database/entities/admin.entity'; //import { AccountStatus, IAdmin, AdminRole, Admin } from '../database/entities/admin.entity'; +import { JwtService } from '@nestjs/jwt'; + @Injectable() export class AuthService { constructor( @Inject('AdminRepository') private readonly adminRepository: AdminRepository, + private readonly jwtService: JwtService, ) {} public async registerAdmin(req: AdminDTO): Promise<{ message: string }> { @@ -25,14 +29,36 @@ export class AuthService { // Hash the password const hashedPassword = await hashString(password); - + // Save the new admin to the database await this.adminRepository.add({ name, email, password: hashedPassword }); - + return { message: 'Registration successful' }; } catch (error) { console.error('Error in registerAdmin:', error); throw new InternalServerErrorException('Failed to register admin'); } } + + public async loginAdmin(req: AuthAdminDTO): Promise<{ isSuccess: boolean, message?: string, token?: string }> { + const { email, password } = req; + + const existingAdmin = await this.adminRepository.findOneByEmail(email); + + if (!existingAdmin) { + throw new ConflictException('Invalid email or password'); + } + + const isPasswordValid = await compareHash(password, existingAdmin.password); + + if (!isPasswordValid) { + throw new ConflictException('Invalid email or password'); + } + + const payload = { email: existingAdmin.email, admin_id: existingAdmin.admin_id }; + const token = this.jwtService.sign(payload); + + return { isSuccess: true, message: 'Login successful', token }; + + } } diff --git a/src/auth/dto/authdto.ts b/src/auth/dto/authdto.ts index dd0727d..e010cc9 100644 --- a/src/auth/dto/authdto.ts +++ b/src/auth/dto/authdto.ts @@ -1,4 +1,4 @@ -import { IsEmail, IsNotEmpty, IsString } from 'class-validator'; +import { IsEmail, IsNotEmpty, IsString, IsStrongPassword } from 'class-validator'; export class AdminDTO { @IsNotEmpty() @@ -12,4 +12,28 @@ export class AdminDTO { @IsNotEmpty() @IsString() password: string; -} \ No newline at end of file +} + + +export class AuthAdminDTO { + @IsString({ message: "Email is required" }) + email!: string; + + @IsStrongPassword({ minLength: 8, minLowercase: 1, minUppercase: 1, minNumbers: 1, minSymbols: 1 }, { message: "Password should be a minimum of 8 characters, with at least 1 uppercase, 1 lowercase, 1 number and 1 special character" }) + password!: string; +} + +// export class LoginUserResponseDTO { +// @IsString() +// isSuccess!: boolean; + +// user?: IUser|null; + +// @IsString() +// token?: string|undefined|null; + +// @IsString() +// message?: string +// } + + diff --git a/src/common/repository/adminRepository.ts b/src/common/repository/adminRepository.ts index a350099..0556064 100644 --- a/src/common/repository/adminRepository.ts +++ b/src/common/repository/adminRepository.ts @@ -9,7 +9,7 @@ export class AdminRepository { private readonly entity: Repository; constructor() { - this.entity = dataSource.getRepository(Admin); // Use dataSource to get the repository + this.entity = dataSource.getRepository(Admin); } async findOneByEmail(email: string): Promise { @@ -19,7 +19,7 @@ export class AdminRepository { async add(adminData: Partial): Promise { const admin = this.entity.create({ ...adminData, - admin_id: uuidv4(), // Generate a unique ID for admin_id + admin_id: uuidv4(), }); return await this.entity.save(admin); } diff --git a/src/database/datasource.ts b/src/database/datasource.ts index faf70d6..104180e 100644 --- a/src/database/datasource.ts +++ b/src/database/datasource.ts @@ -33,7 +33,7 @@ export const postgresLoader = async () => { return conn; } catch (err) { console.error(`❌ Database connection error: ${err}`); - console.log(config().database); + console.log(configs().database); throw err; } }; diff --git a/src/database/entities/admin.entity.ts b/src/database/entities/admin.entity.ts index ca065a5..0c41efd 100644 --- a/src/database/entities/admin.entity.ts +++ b/src/database/entities/admin.entity.ts @@ -14,7 +14,7 @@ export enum AdminRole { } export interface IAdmin{ - _id: string; + admin_id: string; email: string; password: string; status: AccountStatus; diff --git a/src/helpers/utils.ts b/src/helpers/utils.ts index dc20938..53b10d0 100644 --- a/src/helpers/utils.ts +++ b/src/helpers/utils.ts @@ -4,7 +4,8 @@ import Chance from "chance"; const bcrypt = require("bcrypt"); import jwt from "jsonwebtoken" import { configs } from "../config/config.service"; -//import { uuid } from "uuidv4"; +import { uuid } from "uuidv4"; +import moment from "moment"; // const chance = new Chance(); @@ -39,10 +40,10 @@ import { configs } from "../config/config.service"; /** * generateJWT */ - export function generateJWT(email: string, user_id: string) { + export function generateJWT(email: string, admin_id: string) { const jwtData = { email, - user_id + admin_id } return jwt.sign({ jwtData }, configs().JWT_TOKEN.JWT_SECRET, { expiresIn: '3600s', @@ -51,15 +52,15 @@ import { configs } from "../config/config.service"; }); } - // export function generateUUID() { - // const currentTime = moment(); - // const lifeSpan = 24 - // const expiresAt = currentTime.add(lifeSpan,'hours').format() as unknown as Date; - // return { - // uuid: uuid(), - // expiresAt - // } - // } + export function generateUUID() { + const currentTime = moment(); + const lifeSpan = 24 + const expiresAt = currentTime.add(lifeSpan,'hours').format() as unknown as Date; + return { + uuid: uuid(), + expiresAt + } + } // export function hasExpired(expectedExpirationDate: any){