From 54992f569659cc08e0d470365d92b89cb53acf67 Mon Sep 17 00:00:00 2001 From: alexeh Date: Sun, 8 Sep 2024 14:40:35 +0200 Subject: [PATCH] fix test env route, add signup tests --- api/package.json | 4 + api/src/app.controller.spec.ts | 22 -- api/src/app.module.ts | 4 +- api/src/modules/auth/auth.module.ts | 4 +- .../authentication.controller.ts | 24 +- .../authentication/authentication.module.ts | 4 +- .../authentication/authentication.service.ts | 42 ++- api/src/modules/auth/dtos/login.dto.ts | 8 + api/src/modules/auth/guards/jwt-auth.guard.ts | 2 +- .../modules/auth/guards/local-auth.guard.ts | 5 + .../modules/auth/strategies/local.strategy.ts | 58 ++-- api/src/modules/config/app-config.module.ts | 14 +- api/src/modules/config/app-config.service.ts | 5 + api/src/modules/config/path-resolver.ts | 18 ++ api/src/modules/users/users.service.ts | 16 +- api/test/app.e2e-spec.ts | 24 -- api/test/auth/auth.spec.ts | 61 +++++ api/test/utils/db-helpers.ts | 53 ++++ api/test/utils/mocks/entity-mocks.ts | 20 ++ api/test/utils/test-manager.ts | 91 ++++++ api/test/utils/user.auth.ts | 20 ++ pnpm-lock.yaml | 258 +++++++++++++++++- pnpm-workspace.yaml | 9 +- tsconfig.json | 1 - 24 files changed, 664 insertions(+), 103 deletions(-) delete mode 100644 api/src/app.controller.spec.ts create mode 100644 api/src/modules/auth/dtos/login.dto.ts create mode 100644 api/src/modules/auth/guards/local-auth.guard.ts create mode 100644 api/src/modules/config/path-resolver.ts delete mode 100644 api/test/app.e2e-spec.ts create mode 100644 api/test/auth/auth.spec.ts create mode 100644 api/test/utils/db-helpers.ts create mode 100644 api/test/utils/mocks/entity-mocks.ts create mode 100644 api/test/utils/test-manager.ts create mode 100644 api/test/utils/user.auth.ts diff --git a/api/package.json b/api/package.json index bc5a8602..48de8541 100644 --- a/api/package.json +++ b/api/package.json @@ -23,7 +23,9 @@ "@nestjs/passport": "^10.0.3", "@nestjs/platform-express": "^10.0.0", "@nestjs/typeorm": "^10.0.2", + "bcrypt": "catalog:", "class-transformer": "catalog:", + "lodash": "^4.17.21", "passport": "^0.7.0", "passport-jwt": "^4.0.1", "passport-local": "^1.0.0", @@ -36,8 +38,10 @@ "@nestjs/cli": "^10.0.0", "@nestjs/schematics": "^10.0.0", "@nestjs/testing": "^10.0.0", + "@types/bcrypt": "^5.0.2", "@types/express": "^4.17.17", "@types/jest": "^29.5.2", + "@types/lodash": "^4.17.7", "@types/node": "^20.3.1", "@types/passport-jwt": "^4.0.1", "@types/passport-local": "^1.0.38", diff --git a/api/src/app.controller.spec.ts b/api/src/app.controller.spec.ts deleted file mode 100644 index b9bf1896..00000000 --- a/api/src/app.controller.spec.ts +++ /dev/null @@ -1,22 +0,0 @@ -import { Test, TestingModule } from '@nestjs/testing'; -import { AppController } from './app.controller'; -import { AppService } from './app.service'; - -describe('AppController', () => { - let appController: AppController; - - beforeEach(async () => { - const app: TestingModule = await Test.createTestingModule({ - controllers: [AppController], - providers: [AppService], - }).compile(); - - appController = app.get(AppController); - }); - - describe('root', () => { - it('test', () => { - expect(appController.getHello()).toBe('Hello Blue Coast Carbon Tool!'); - }); - }); -}); diff --git a/api/src/app.module.ts b/api/src/app.module.ts index ed979d35..38ceba90 100644 --- a/api/src/app.module.ts +++ b/api/src/app.module.ts @@ -3,8 +3,8 @@ import { AppController } from './app.controller'; import { AppService } from './app.service'; import { ApiConfigModule } from '@api/modules/config/app-config.module'; import { APP_GUARD } from '@nestjs/core'; -import { JwtAuthGuard } from '@auth/guards/jwt-auth.guard'; -import { AuthModule } from '@auth/auth.module'; +import { AuthModule } from '@api/modules/auth/auth.module'; +import { JwtAuthGuard } from '@api/modules/auth/guards/jwt-auth.guard'; @Module({ imports: [ApiConfigModule, AuthModule], diff --git a/api/src/modules/auth/auth.module.ts b/api/src/modules/auth/auth.module.ts index fcd2d337..1a801034 100644 --- a/api/src/modules/auth/auth.module.ts +++ b/api/src/modules/auth/auth.module.ts @@ -1,6 +1,6 @@ import { Module } from '@nestjs/common'; -import { AuthenticationModule } from '@auth/authentication/authentication.module'; -import { AuthorisationModule } from '@auth/authorisation/authorisation.module'; +import { AuthenticationModule } from '@api/modules/auth/authentication/authentication.module'; +import { AuthorisationModule } from '@api/modules/auth/authorisation/authorisation.module'; @Module({ imports: [AuthenticationModule, AuthorisationModule], diff --git a/api/src/modules/auth/authentication/authentication.controller.ts b/api/src/modules/auth/authentication/authentication.controller.ts index b81a9489..33fc5a6a 100644 --- a/api/src/modules/auth/authentication/authentication.controller.ts +++ b/api/src/modules/auth/authentication/authentication.controller.ts @@ -1,4 +1,24 @@ -import { Controller } from '@nestjs/common'; +import { Body, Controller, Post, UseGuards } from '@nestjs/common'; +import { User } from '@shared/entities/users/user.entity'; +import { AuthenticationService } from '@api/modules/auth/authentication/authentication.service'; +import { LoginDto } from '@api/modules/auth/dtos/login.dto'; +import { LocalAuthGuard } from '@api/modules/auth/guards/local-auth.guard'; +import { GetUser } from '@api/modules/auth/decorators/get-user.decorator'; +import { Public } from '@api/modules/auth/decorators/is-public.decorator'; @Controller('authentication') -export class AuthenticationController {} +export class AuthenticationController { + constructor(private authService: AuthenticationService) {} + + @Public() + @Post('signup') + async signup(@Body() signupDto: LoginDto) { + return this.authService.signup(signupDto); + } + + @UseGuards(LocalAuthGuard) + @Post('login') + async login(@GetUser() user: User) { + return this.authService.signIn(user); + } +} diff --git a/api/src/modules/auth/authentication/authentication.module.ts b/api/src/modules/auth/authentication/authentication.module.ts index ed06daae..ace6753b 100644 --- a/api/src/modules/auth/authentication/authentication.module.ts +++ b/api/src/modules/auth/authentication/authentication.module.ts @@ -5,9 +5,10 @@ import { PassportModule } from '@nestjs/passport'; import { JwtModule } from '@nestjs/jwt'; import { ApiConfigModule } from '@api/modules/config/app-config.module'; import { ApiConfigService } from '@api/modules/config/app-config.service'; -import { JwtStrategy } from '@auth/strategies/jwt.strategy'; import { UsersService } from '@api/modules/users/users.service'; import { UsersModule } from '@api/modules/users/users.module'; +import { LocalStrategy } from '@api/modules/auth/strategies/local.strategy'; +import { JwtStrategy } from '@api/modules/auth/strategies/jwt.strategy'; @Module({ imports: [ @@ -24,6 +25,7 @@ import { UsersModule } from '@api/modules/users/users.module'; ], providers: [ AuthenticationService, + LocalStrategy, { provide: JwtStrategy, useFactory: (users: UsersService, config: ApiConfigService) => { diff --git a/api/src/modules/auth/authentication/authentication.service.ts b/api/src/modules/auth/authentication/authentication.service.ts index 8e234fb8..c5565ce4 100644 --- a/api/src/modules/auth/authentication/authentication.service.ts +++ b/api/src/modules/auth/authentication/authentication.service.ts @@ -1,4 +1,42 @@ -import { Injectable } from '@nestjs/common'; +import { Injectable, UnauthorizedException } from '@nestjs/common'; +import { JwtService } from '@nestjs/jwt'; +import { UsersService } from '@api/modules/users/users.service'; +import { User } from '@shared/entities/users/user.entity'; +import * as bcrypt from 'bcrypt'; +import { LoginDto } from '@api/modules/auth/dtos/login.dto'; +import { JwtPayload } from '@api/modules/auth/strategies/jwt.strategy'; @Injectable() -export class AuthenticationService {} +export class AuthenticationService { + constructor( + private readonly usersService: UsersService, + private readonly jwt: JwtService, + ) {} + async validateUser(email: string, password: string): Promise { + const user = await this.usersService.findByEmail(email); + if (user && (await bcrypt.compare(password, user.password))) { + return user; + } + throw new UnauthorizedException(`Invalid credentials`); + } + + async signup(signupDto: LoginDto): Promise { + const passwordHash = await bcrypt.hash(signupDto.password, 10); + await this.usersService.createUser({ + email: signupDto.email, + password: passwordHash, + }); + } + + async login(loginDto: LoginDto): Promise<{ access_token: string }> { + const user = await this.validateUser(loginDto.email, loginDto.password); + return { + access_token: this.jwt.sign({ id: user.id }), + }; + } + async signIn(user: User): Promise<{ user: User; accessToken: string }> { + const payload: JwtPayload = { id: user.id }; + const accessToken: string = this.jwt.sign(payload); + return { user, accessToken }; + } +} diff --git a/api/src/modules/auth/dtos/login.dto.ts b/api/src/modules/auth/dtos/login.dto.ts new file mode 100644 index 00000000..fe819985 --- /dev/null +++ b/api/src/modules/auth/dtos/login.dto.ts @@ -0,0 +1,8 @@ +/** + * @note: Depending on how we will proceed with the repo structure, we might need to move this file to the shared module. + */ + +export class LoginDto { + email: string; + password: string; +} diff --git a/api/src/modules/auth/guards/jwt-auth.guard.ts b/api/src/modules/auth/guards/jwt-auth.guard.ts index 5e0ea144..5fc3731a 100644 --- a/api/src/modules/auth/guards/jwt-auth.guard.ts +++ b/api/src/modules/auth/guards/jwt-auth.guard.ts @@ -2,7 +2,7 @@ import { Injectable, ExecutionContext } from '@nestjs/common'; import { AuthGuard } from '@nestjs/passport'; import { Reflector } from '@nestjs/core'; import { Observable } from 'rxjs'; -import { IS_PUBLIC_KEY } from '@auth/decorators/is-public.decorator'; +import { IS_PUBLIC_KEY } from '@api/modules/auth/decorators/is-public.decorator'; @Injectable() export class JwtAuthGuard extends AuthGuard('jwt') { diff --git a/api/src/modules/auth/guards/local-auth.guard.ts b/api/src/modules/auth/guards/local-auth.guard.ts new file mode 100644 index 00000000..ccf962b6 --- /dev/null +++ b/api/src/modules/auth/guards/local-auth.guard.ts @@ -0,0 +1,5 @@ +import { Injectable } from '@nestjs/common'; +import { AuthGuard } from '@nestjs/passport'; + +@Injectable() +export class LocalAuthGuard extends AuthGuard('local') {} diff --git a/api/src/modules/auth/strategies/local.strategy.ts b/api/src/modules/auth/strategies/local.strategy.ts index 88cded00..59c5207d 100644 --- a/api/src/modules/auth/strategies/local.strategy.ts +++ b/api/src/modules/auth/strategies/local.strategy.ts @@ -1,29 +1,29 @@ -// import { Injectable, UnauthorizedException } from '@nestjs/common'; -// import { PassportStrategy } from '@nestjs/passport'; -// -// import { Strategy } from 'passport-local'; -// import { User } from '@shared/dto/users/user.entity'; -// import { AuthService } from '@api/modules/auth/auth.service'; -// -// /** -// * @description: LocalStrategy is used by passport to authenticate by email and password rather than a token. -// */ -// -// @Injectable() -// export class LocalStrategy extends PassportStrategy(Strategy) { -// constructor(private readonly authService: AuthService) { -// super({ usernameField: 'email' }); -// } -// -// async validate(email: string, password: string): Promise { -// const user: User | null = await this.authService.validateUser( -// email, -// password, -// ); -// -// if (!user) { -// throw new UnauthorizedException(); -// } -// return user; -// } -// } +import { Injectable, UnauthorizedException } from '@nestjs/common'; +import { PassportStrategy } from '@nestjs/passport'; + +import { Strategy } from 'passport-local'; +import { User } from '@shared/entities/users/user.entity'; +import { AuthenticationService } from '@api/modules/auth/authentication/authentication.service'; + +/** + * @description: LocalStrategy is used by passport to authenticate by email and password rather than a token. + */ + +@Injectable() +export class LocalStrategy extends PassportStrategy(Strategy) { + constructor(private readonly authService: AuthenticationService) { + super({ usernameField: 'email' }); + } + + async validate(email: string, password: string): Promise { + const user: User | null = await this.authService.validateUser( + email, + password, + ); + + if (!user) { + throw new UnauthorizedException(); + } + return user; + } +} diff --git a/api/src/modules/config/app-config.module.ts b/api/src/modules/config/app-config.module.ts index 9fb05ac5..0aa60ef5 100644 --- a/api/src/modules/config/app-config.module.ts +++ b/api/src/modules/config/app-config.module.ts @@ -1,13 +1,13 @@ -import { Global, Module } from '@nestjs/common'; +import { Global, Module, OnModuleInit } from '@nestjs/common'; import { ConfigModule, ConfigService } from '@nestjs/config'; import { join } from 'path'; import { ApiConfigService } from '@api/modules/config/app-config.service'; import { DatabaseModule } from '@api/modules/config/database/database.module'; +import { resolveConfigPath } from '@api/modules/config/path-resolver'; @Global() @Module({ imports: [ - DatabaseModule, /** * @note: Check if we can abstract the conf to ApiConfigService */ @@ -16,15 +16,13 @@ import { DatabaseModule } from '@api/modules/config/database/database.module'; cache: true, // TODO: This is a bit ugly, we should find a way to make this more elegant envFilePath: [ - join( - __dirname, - `../../../../../../shared/config/.env.${process.env.NODE_ENV}`, - ), - join(__dirname, '../../../../../../shared/config/.env'), + resolveConfigPath(`shared/config/.env.${process.env.NODE_ENV}`), + resolveConfigPath(`shared/config/.env`), ], }), + DatabaseModule, ], providers: [ConfigService, ApiConfigService], - exports: [ApiConfigService, DatabaseModule], + exports: [ApiConfigService], }) export class ApiConfigModule {} diff --git a/api/src/modules/config/app-config.service.ts b/api/src/modules/config/app-config.service.ts index 1fec47e8..25da0f87 100644 --- a/api/src/modules/config/app-config.service.ts +++ b/api/src/modules/config/app-config.service.ts @@ -1,6 +1,8 @@ import { Injectable } from '@nestjs/common'; import { ConfigService } from '@nestjs/config'; import { DATABASE_ENTITIES } from '@shared/entities/database.entities'; +import { readdirSync } from 'fs'; +import { join } from 'path'; export type JWTConfig = { secret: string; @@ -18,6 +20,9 @@ export class ApiConfigService { * @note: Maybe it's a good idea to move the datasource config to shared folder, to be used potentially for a e2e test agent */ getDatabaseConfig() { + console.log('ALL ENVSSS'); + console.log(readdirSync(join(__dirname, '../../../../shared/config'))); + return { host: this.configService.get('DB_HOST'), port: this.configService.get('DB_PORT'), diff --git a/api/src/modules/config/path-resolver.ts b/api/src/modules/config/path-resolver.ts new file mode 100644 index 00000000..96ef3e2b --- /dev/null +++ b/api/src/modules/config/path-resolver.ts @@ -0,0 +1,18 @@ +import { join } from 'path'; +import { readdirSync } from 'fs'; + +// TODO: Workaround: This should be prob fixed in the jest conf + +const TEST_RELATIVE_PATH = '../../../../'; +const DEFAULT_RELATIVE_PATH = '../../../../../../'; + +/** + * @description: Resolve the path of the config file depending on the environment + */ +export function resolveConfigPath(relativePath: string): string { + const rootDir = + process.env.NODE_ENV === 'test' + ? TEST_RELATIVE_PATH + : DEFAULT_RELATIVE_PATH; + return join(__dirname, rootDir, relativePath); +} diff --git a/api/src/modules/users/users.service.ts b/api/src/modules/users/users.service.ts index 815746f4..999f64ed 100644 --- a/api/src/modules/users/users.service.ts +++ b/api/src/modules/users/users.service.ts @@ -1,4 +1,4 @@ -import { Injectable } from '@nestjs/common'; +import { ConflictException, Injectable } from '@nestjs/common'; import { InjectRepository } from '@nestjs/typeorm'; import { User } from '@shared/entities/users/user.entity'; import { Repository } from 'typeorm'; @@ -12,4 +12,18 @@ export class UsersService { where: { id }, }); } + + async findByEmail(email: string): Promise { + return this.repo.findOne({ where: { email } }); + } + + async createUser(createUserDto: { email: string; password: string }) { + const existingUser = await this.findByEmail(createUserDto.email); + if (existingUser) { + throw new ConflictException( + `Email ${createUserDto.email} already exists`, + ); + } + return this.repo.save(createUserDto); + } } diff --git a/api/test/app.e2e-spec.ts b/api/test/app.e2e-spec.ts deleted file mode 100644 index feda005c..00000000 --- a/api/test/app.e2e-spec.ts +++ /dev/null @@ -1,24 +0,0 @@ -import { Test, TestingModule } from '@nestjs/testing'; -import { INestApplication } from '@nestjs/common'; -import * as request from 'supertest'; -import { AppModule } from '@api/app.module'; - -describe('AppController (e2e)', () => { - let app: INestApplication; - - beforeEach(async () => { - const moduleFixture: TestingModule = await Test.createTestingModule({ - imports: [AppModule], - }).compile(); - - app = moduleFixture.createNestApplication(); - await app.init(); - }); - - it('/ (GET)', () => { - return request(app.getHttpServer()) - .get('/') - .expect(200) - .expect('Hello Blue Coast Carbon Tool!'); - }); -}); diff --git a/api/test/auth/auth.spec.ts b/api/test/auth/auth.spec.ts new file mode 100644 index 00000000..ef1c04d2 --- /dev/null +++ b/api/test/auth/auth.spec.ts @@ -0,0 +1,61 @@ +import { TestManager } from '../utils/test-manager'; + +import { User } from '@shared/entities/users/user.entity'; + +describe('Authentication', () => { + let testManager: TestManager; + + beforeAll(async () => { + console.log('ENV', process.env.NODE_ENV); + testManager = await TestManager.createTestManager(); + }); + + afterEach(async () => { + await testManager.clearDatabase(); + }); + + afterAll(async () => { + await testManager.close(); + }); + describe('Sign Up', () => { + test.skip(`it should throw validation errors`, async () => {}); + test(`it should throw email already exist error`, async () => { + const user = await testManager.mocks().createUser({}); + const response = await testManager + .request() + .post('/authentication/signup') + .send({ + email: user.email, + password: user.password, + }); + expect(response.status).toBe(409); + expect(response.body.message).toEqual( + `Email ${user.email} already exists`, + ); + console.log('RESPONSE', response.body); + }); + test(`it should sign up a new user`, async () => { + const newUser = { email: 'test@test.com', password: '12345678' }; + const response = await testManager + .request() + .post('/authentication/signup') + .send({ + email: newUser.email, + password: newUser.password, + }); + const user = await testManager + .getDataSource() + .getRepository(User) + .findOne({ + where: { email: newUser.email }, + }); + expect(user.id).toBeDefined(); + expect(user.email).toEqual(newUser.email); + }); + }); + describe('Sign In', () => { + test(`it should throw an error if no user exists with provided credentials`, async () => {}); + test(`it should throw an error if password is incorrect`, async () => {}); + test(`it should sign in a user`, async () => {}); + }); +}); diff --git a/api/test/utils/db-helpers.ts b/api/test/utils/db-helpers.ts new file mode 100644 index 00000000..28b205b2 --- /dev/null +++ b/api/test/utils/db-helpers.ts @@ -0,0 +1,53 @@ +import { DataSource, EntityMetadata } from 'typeorm'; +import { difference } from 'lodash'; + +export async function clearTestDataFromDatabase( + dataSource: DataSource, +): Promise { + const queryRunner = dataSource.createQueryRunner(); + await queryRunner.connect(); + await queryRunner.startTransaction(); + try { + const entityTableNames: string[] = dataSource.entityMetadatas + .filter( + (entityMetadata: EntityMetadata) => + entityMetadata.tableType === 'regular' || + entityMetadata.tableType === 'junction', + ) + .map((entityMetadata: EntityMetadata) => entityMetadata.tableName); + + await Promise.all( + entityTableNames.map((entityTableName: string) => + queryRunner.query(`TRUNCATE TABLE "${entityTableName}" CASCADE`), + ), + ); + + entityTableNames.push(dataSource.metadataTableName); + entityTableNames.push( + dataSource.options.migrationsTableName || 'migrations', + ); + entityTableNames.push('spatial_ref_sys'); + + const databaseTableNames: string[] = ( + await dataSource.query( + `SELECT table_name FROM information_schema.tables WHERE table_schema='public' AND table_type='BASE TABLE'`, + ) + ).map((e: Record) => e.table_name); + + const tablesToDrop = difference(databaseTableNames, entityTableNames); + + await Promise.all( + tablesToDrop.map((tableToDrop: string) => + queryRunner.dropTable(tableToDrop), + ), + ); + await queryRunner.commitTransaction(); + } catch (err) { + // rollback changes before throwing error + await queryRunner.rollbackTransaction(); + throw err; + } finally { + // release query runner which is manually created + await queryRunner.release(); + } +} diff --git a/api/test/utils/mocks/entity-mocks.ts b/api/test/utils/mocks/entity-mocks.ts new file mode 100644 index 00000000..f5876e61 --- /dev/null +++ b/api/test/utils/mocks/entity-mocks.ts @@ -0,0 +1,20 @@ +import { DataSource, DeepPartial } from 'typeorm'; +import { User } from '@shared/entities/users/user.entity'; +import { genSalt, hash } from 'bcrypt'; + +export const createUser = async ( + dataSource: DataSource, + additionalData?: Partial, +): Promise => { + const salt = await genSalt(); + const usedPassword = additionalData?.password ?? '12345678'; + const defaultData: DeepPartial = { + email: 'test@user.com', + password: await hash(usedPassword, salt), + }; + + const user = { ...defaultData, ...additionalData }; + + await dataSource.getRepository(User).save(user); + return { ...user, password: usedPassword } as User; +}; diff --git a/api/test/utils/test-manager.ts b/api/test/utils/test-manager.ts new file mode 100644 index 00000000..1227f7b9 --- /dev/null +++ b/api/test/utils/test-manager.ts @@ -0,0 +1,91 @@ +import { AppModule } from '@api/app.module'; +import { Test, TestingModule } from '@nestjs/testing'; +import { INestApplication, ValidationPipe } from '@nestjs/common'; +import { DataSource } from 'typeorm'; + +import { logUserIn } from './user.auth'; +import { Type } from '@nestjs/common/interfaces'; +import * as request from 'supertest'; + +import { getDataSourceToken } from '@nestjs/typeorm'; +import { clearTestDataFromDatabase } from './db-helpers'; +import { createUser } from './mocks/entity-mocks'; +import { User } from '@shared/entities/users/user.entity'; +import { ApiConfigModule } from '@api/modules/config/app-config.module'; +import { ApiConfigService } from '@api/modules/config/app-config.service'; + +/** + * @description: Abstraction for NestJS testing workflow. For now its a basic implementation to create a test app, but can be extended to encapsulate + * common testing utilities + */ + +export class TestManager { + testApp: INestApplication; + dataSource: DataSource; + moduleFixture: TestingModule; + constructor( + testApp: INestApplication, + dataSource: DataSource, + moduleFixture: TestingModule, + ) { + this.testApp = testApp; + this.dataSource = dataSource; + this.moduleFixture = moduleFixture; + } + + static async createTestManager(options?: { + fixtures: FixtureType; + }) { + const moduleFixture = await Test.createTestingModule({ + imports: [AppModule], + }).compile(); + const dataSource = moduleFixture.get(getDataSourceToken()); + const testApp = moduleFixture.createNestApplication(); + // TODO: Add global validation or App level Zod when decided what to use + //testApp.useGlobalPipes(new ValidationPipe()); + await testApp.init(); + return new TestManager(testApp, dataSource, moduleFixture); + } + + async clearDatabase() { + await clearTestDataFromDatabase(this.dataSource); + } + + getApp() { + return this.testApp; + } + + getDataSource() { + return this.dataSource; + } + + close() { + return this.testApp.close(); + } + + getModule( + typeOrToken: Type | Function | string | symbol, + ): TResult { + return this.moduleFixture.get(typeOrToken); + } + + async setUpTestUser() { + const user = await createUser(this.getDataSource()); + return logUserIn(this, user); + } + + async logUserIn(user: Partial) { + return logUserIn(this, user); + } + + request() { + return request(this.testApp.getHttpServer()); + } + + mocks() { + return { + createUser: (additionalData: Partial) => + createUser(this.getDataSource(), additionalData), + }; + } +} diff --git a/api/test/utils/user.auth.ts b/api/test/utils/user.auth.ts new file mode 100644 index 00000000..0842925a --- /dev/null +++ b/api/test/utils/user.auth.ts @@ -0,0 +1,20 @@ +import * as request from 'supertest'; +import { TestManager } from './test-manager'; +import { User } from '@shared/entities/users/user.entity'; + +export type TestUser = { jwtToken: string; user: User; password: string }; + +export async function logUserIn( + testManager: TestManager, + user: Partial, +): Promise { + const response = await request(testManager.getApp().getHttpServer()) + .post('/authentication/login') + .send({ email: user.email, password: user.password }); + + return { + jwtToken: response.body.accessToken, + user: user as User, + password: user.password, + }; +} diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index b30a7deb..ea128a5a 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -6,17 +6,20 @@ settings: catalogs: default: + bcrypt: + specifier: 5.1.1 + version: 5.1.1 class-transformer: - specifier: ^0.5.1 + specifier: 0.5.1 version: 0.5.1 pg: - specifier: ^8.12.0 + specifier: 8.12.0 version: 8.12.0 typeorm: - specifier: ^0.3.20 + specifier: 0.3.20 version: 0.3.20 zod: - specifier: ^3.23.8 + specifier: 3.23.8 version: 3.23.8 importers: @@ -46,9 +49,15 @@ importers: '@nestjs/typeorm': specifier: ^10.0.2 version: 10.0.2(@nestjs/common@10.4.1(class-transformer@0.5.1)(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/core@10.4.1(@nestjs/common@10.4.1(class-transformer@0.5.1)(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/platform-express@10.4.1)(reflect-metadata@0.2.2)(rxjs@7.8.1))(reflect-metadata@0.2.2)(rxjs@7.8.1)(typeorm@0.3.20(pg@8.12.0)(ts-node@10.9.2(@types/node@20.16.5)(typescript@5.5.4))) + bcrypt: + specifier: 'catalog:' + version: 5.1.1 class-transformer: specifier: 'catalog:' version: 0.5.1 + lodash: + specifier: ^4.17.21 + version: 4.17.21 passport: specifier: ^0.7.0 version: 0.7.0 @@ -80,12 +89,18 @@ importers: '@nestjs/testing': specifier: ^10.0.0 version: 10.4.1(@nestjs/common@10.4.1(class-transformer@0.5.1)(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/core@10.4.1(@nestjs/common@10.4.1(class-transformer@0.5.1)(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/platform-express@10.4.1)(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/platform-express@10.4.1(@nestjs/common@10.4.1(class-transformer@0.5.1)(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/core@10.4.1)) + '@types/bcrypt': + specifier: ^5.0.2 + version: 5.0.2 '@types/express': specifier: ^4.17.17 version: 4.17.21 '@types/jest': specifier: ^29.5.2 version: 29.5.12 + '@types/lodash': + specifier: ^4.17.7 + version: 4.17.7 '@types/node': specifier: ^20.3.1 version: 20.16.5 @@ -540,6 +555,10 @@ packages: resolution: {integrity: sha512-Z7C/xXCiGWsg0KuKsHTKJxbWhpI3Vs5GwLfOean7MGyVFGqdRgBbAjOCh6u4bbjPc/8MJ2pZmK/0DLdCbivLDA==} engines: {node: '>=8'} + '@mapbox/node-pre-gyp@1.0.11': + resolution: {integrity: sha512-Yhlar6v9WQgUp/He7BdgzOz8lqMQ8sU+jkCq7Wx8Myc5YFJLbEe7lgui/V7G1qB1DJykHSGwreceSaD60Y0PUQ==} + hasBin: true + '@nestjs/cli@10.4.5': resolution: {integrity: sha512-FP7Rh13u8aJbHe+zZ7hM0CC4785g9Pw4lz4r2TTgRtf0zTxSWMkJaPEwyjX8SK9oWK2GsYxl+fKpwVZNbmnj9A==} engines: {node: '>= 16.14'} @@ -770,6 +789,9 @@ packages: '@types/babel__traverse@7.20.6': resolution: {integrity: sha512-r1bzfrm0tomOI8g1SzvCaQHo6Lcv6zu0EA+W2kHrt8dyrHQxGzBBL4kdkzIS+jBMV+EYcMAEAqXqYaLJq5rOZg==} + '@types/bcrypt@5.0.2': + resolution: {integrity: sha512-6atioO8Y75fNcbmj0G7UjI9lXN2pQ/IGJ2FWT4a/btd0Lk9lQalHLKhkgKVZ3r+spnmWUKfbMi1GEe9wyHQfNQ==} + '@types/body-parser@1.19.5': resolution: {integrity: sha512-fB3Zu92ucau0iQ0JMCFQE7b/dv8Ot07NI3KaZIkIUNXq82k4eBAqUaneXfleGY9JWskeS9y+u0nXMyspcuQrCg==} @@ -815,6 +837,9 @@ packages: '@types/jsonwebtoken@9.0.5': resolution: {integrity: sha512-VRLSGzik+Unrup6BsouBeHsf4d1hOEgYWTm/7Nmw1sXoN1+tRly/Gy/po3yeahnP4jfnQWWAhQAqcNfH7ngOkA==} + '@types/lodash@4.17.7': + resolution: {integrity: sha512-8wTvZawATi/lsmNu10/j2hk1KEP0IvjubqPE3cu1Xz7xfXXt5oCq3SNUz4fMIP4XGF9Ky+Ue2tBA3hcS7LSBlA==} + '@types/methods@1.1.4': resolution: {integrity: sha512-ymXWVrDiCxTBE3+RIrrP533E70eA+9qu7zdWoHuOmGujkYtzf4HQF96b8nwHLqhuf4ykX61IGRIB38CC6/sImQ==} @@ -1045,6 +1070,9 @@ packages: '@xtuc/long@4.2.2': resolution: {integrity: sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ==} + abbrev@1.1.1: + resolution: {integrity: sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==} + accepts@1.3.8: resolution: {integrity: sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==} engines: {node: '>= 0.6'} @@ -1068,6 +1096,10 @@ packages: engines: {node: '>=0.4.0'} hasBin: true + agent-base@6.0.2: + resolution: {integrity: sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==} + engines: {node: '>= 6.0.0'} + ajv-formats@2.1.1: resolution: {integrity: sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA==} peerDependencies: @@ -1133,6 +1165,14 @@ packages: append-field@1.0.0: resolution: {integrity: sha512-klpgFSWLW1ZEs8svjfb7g4qWY0YS5imI82dTg+QahUvJ8YqAY0P10Uk8tTyh9ZGuYEZEMaeJYCF5BFuX552hsw==} + aproba@2.0.0: + resolution: {integrity: sha512-lYe4Gx7QT+MKGbDsA+Z+he/Wtef0BiwDOlK/XkBrdfsh9J/jPPXbX0tE9x9cl27Tmu5gg3QUbUrQYa/y+KOHPQ==} + + are-we-there-yet@2.0.0: + resolution: {integrity: sha512-Ci/qENmwHnsYo9xKIcUJN5LeDKdJ6R1Z1j9V/J5wyq8nh/mYPEpIKJbBZXtZjG04HiK7zV/p6Vs9952MrMeUIw==} + engines: {node: '>=10'} + deprecated: This package is no longer supported. + arg@4.1.3: resolution: {integrity: sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==} @@ -1245,6 +1285,10 @@ packages: base64-js@1.5.1: resolution: {integrity: sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==} + bcrypt@5.1.1: + resolution: {integrity: sha512-AGBHOG5hPYZ5Xl9KXzU5iKq9516yEmvCKDg3ecP5kX2aB6UqTeXZxk2ELnDgDm6BQSMlLt9rDB4LoSMx0rYwww==} + engines: {node: '>= 10.0.0'} + binary-extensions@2.3.0: resolution: {integrity: sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==} engines: {node: '>=8'} @@ -1344,6 +1388,10 @@ packages: resolution: {integrity: sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==} engines: {node: '>= 8.10.0'} + chownr@2.0.0: + resolution: {integrity: sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==} + engines: {node: '>=10'} + chrome-trace-event@1.0.4: resolution: {integrity: sha512-rNjApaLzuwaOTjCiT8lSDdGN1APCiqkChLMJxJPWLunPAt5fy8xgU9/jNOchV84wfIxrA0lRQB7oCT8jrn/wrQ==} engines: {node: '>=6.0'} @@ -1417,6 +1465,10 @@ packages: color-name@1.1.4: resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==} + color-support@1.1.3: + resolution: {integrity: sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg==} + hasBin: true + combined-stream@1.0.8: resolution: {integrity: sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==} engines: {node: '>= 0.8'} @@ -1445,6 +1497,9 @@ packages: consola@2.15.3: resolution: {integrity: sha512-9vAdYbHj6x2fLKC4+oPH0kFzY/orMZyG2Aj+kNylHxKGJ/Ed4dpNyAQYwJOdqO4zdM7XpVHmyejQDcQHrnuXbw==} + console-control-strings@1.1.0: + resolution: {integrity: sha512-ty/fTekppD2fIwRvnZAVdeOiGd1c7YXEixbgJTNzqcxJWKQnjJ/V1bNEEE6hygpM3WjwHFUVK6HTjWSzV4a8sQ==} + content-disposition@0.5.4: resolution: {integrity: sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==} engines: {node: '>= 0.6'} @@ -1579,6 +1634,9 @@ packages: resolution: {integrity: sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==} engines: {node: '>=0.4.0'} + delegates@1.0.0: + resolution: {integrity: sha512-bd2L678uiWATM6m5Z1VzNCErI3jiGzt6HGY8OVICs40JQq/HALfbyNJmp0UDakEY4pMMaN0Ly5om/B1VI/+xfQ==} + depd@2.0.0: resolution: {integrity: sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==} engines: {node: '>= 0.8'} @@ -1587,6 +1645,10 @@ packages: resolution: {integrity: sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==} engines: {node: '>= 0.8', npm: 1.2.8000 || >= 1.4.16} + detect-libc@2.0.3: + resolution: {integrity: sha512-bwy0MGW55bG41VqxxypOsdSdGqLwXPI/focwgTYCFMbdUiBAxLg9CFzG08sz2aqzknwiX7Hkl0bQENjg8iLByw==} + engines: {node: '>=8'} + detect-newline@3.1.0: resolution: {integrity: sha512-TLz+x/vEXm/Y7P7wn1EJFNLxYpUD4TgMosxY6fAVJUnJMbupHBOncxyWUG9OpTaH9EBD7uFI5LfEgmMOc54DsA==} engines: {node: '>=8'} @@ -1982,6 +2044,10 @@ packages: resolution: {integrity: sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==} engines: {node: '>=12'} + fs-minipass@2.1.0: + resolution: {integrity: sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg==} + engines: {node: '>= 8'} + fs-monkey@1.0.6: resolution: {integrity: sha512-b1FMfwetIKymC0eioW7mTywihSQE4oLzQn1dB6rZB5fx/3NpNEdAWeCSMB+60/AeT0TCXsxzAlcYVEFCTAksWg==} @@ -2003,6 +2069,11 @@ packages: functions-have-names@1.2.3: resolution: {integrity: sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==} + gauge@3.0.2: + resolution: {integrity: sha512-+5J6MS/5XksCuXq++uFRsnUd7Ovu1XenbeuIuNRJxYWjgQbPuFhT14lAvsWfqfAmnwluf1OwMjz39HjfLPci0Q==} + engines: {node: '>=10'} + deprecated: This package is no longer supported. + gensync@1.0.0-beta.2: resolution: {integrity: sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==} engines: {node: '>=6.9.0'} @@ -2114,6 +2185,9 @@ packages: resolution: {integrity: sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==} engines: {node: '>= 0.4'} + has-unicode@2.0.1: + resolution: {integrity: sha512-8Rf9Y83NBReMnx0gFzA8JImQACstCYWUplepDa9xprwwtmgEZUF0h/i5xSA625zB/I37EtrswSST6OXxwaaIJQ==} + hasown@2.0.2: resolution: {integrity: sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==} engines: {node: '>= 0.4'} @@ -2132,6 +2206,10 @@ packages: resolution: {integrity: sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==} engines: {node: '>= 0.8'} + https-proxy-agent@5.0.1: + resolution: {integrity: sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==} + engines: {node: '>= 6'} + human-signals@2.1.0: resolution: {integrity: sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==} engines: {node: '>=10.17.0'} @@ -2663,6 +2741,10 @@ packages: resolution: {integrity: sha512-ISQTe55T2ao7XtlAStud6qwYPZjE4GK1S/BeVPus4jrq6JuOnQ00YKQC581RWhR122W7msZV263KzVeLoqidyQ==} engines: {node: '>=12'} + make-dir@3.1.0: + resolution: {integrity: sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==} + engines: {node: '>=8'} + make-dir@4.0.0: resolution: {integrity: sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw==} engines: {node: '>=10'} @@ -2739,14 +2821,31 @@ packages: minimist@1.2.8: resolution: {integrity: sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==} + minipass@3.3.6: + resolution: {integrity: sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==} + engines: {node: '>=8'} + + minipass@5.0.0: + resolution: {integrity: sha512-3FnjYuehv9k6ovOEbyOswadCDPX1piCfhV8ncmYtHOjuPwylVWsghTLo7rabjC3Rx5xD4HDx8Wm1xnMF7S5qFQ==} + engines: {node: '>=8'} + minipass@7.1.2: resolution: {integrity: sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==} engines: {node: '>=16 || 14 >=14.17'} + minizlib@2.1.2: + resolution: {integrity: sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg==} + engines: {node: '>= 8'} + mkdirp@0.5.6: resolution: {integrity: sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==} hasBin: true + mkdirp@1.0.4: + resolution: {integrity: sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==} + engines: {node: '>=10'} + hasBin: true + mkdirp@2.1.6: resolution: {integrity: sha512-+hEnITedc8LAtIP9u3HJDFIdcLV2vXP33sqLLIzkv1Db1zO/1OxbvYf0Y1OC/S/Qo5dxHXepofhmxL02PsKe+A==} engines: {node: '>=10'} @@ -2811,6 +2910,9 @@ packages: node-abort-controller@3.1.1: resolution: {integrity: sha512-AGK2yQKIjRuqnc6VkX2Xj5d+QW8xZ87pa1UK6yA6ouUyuxfHuMP6umE5QK7UmTeOAymo+Zx1Fxiuw9rVx8taHQ==} + node-addon-api@5.1.0: + resolution: {integrity: sha512-eh0GgfEkpnoWDq+VY8OyvYhFEzBk6jIYbRKdIlyTiAXIVJ8PyBaKb0rp7oDtoddbdoHWhq8wwr+XZ81F1rpNdA==} + node-emoji@1.11.0: resolution: {integrity: sha512-wo2DpQkQp7Sjm2A0cq+sN7EHKO6Sl0ctXeBdFZrL9T9+UywORbufTcTZxom8YqpLQt/FqNMUkOpkZrJVYSKD3A==} @@ -2829,6 +2931,11 @@ packages: node-releases@2.0.18: resolution: {integrity: sha512-d9VeXT4SJ7ZeOqGX6R5EM022wpL+eWPooLI+5UpWn2jCT1aosUQEhQP214x33Wkwx3JQMvIm+tIoVOdodFS40g==} + nopt@5.0.0: + resolution: {integrity: sha512-Tbj67rffqceeLpcRXrT7vKAN8CwfPeIBgM7E6iBkmKLV7bEMwpGgYLGv0jACUsECaa/vuxP0IjEont6umdMgtQ==} + engines: {node: '>=6'} + hasBin: true + normalize-path@3.0.0: resolution: {integrity: sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==} engines: {node: '>=0.10.0'} @@ -2837,6 +2944,10 @@ packages: resolution: {integrity: sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==} engines: {node: '>=8'} + npmlog@5.0.1: + resolution: {integrity: sha512-AqZtDUWOMKs1G/8lwylVjrdYgqA4d9nu8hc+0gzRxlDb1I10+FHBGMXs6aiQHFdCUUlqH99MUMuLfzWDNDtfxw==} + deprecated: This package is no longer supported. + object-assign@4.1.1: resolution: {integrity: sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==} engines: {node: '>=0.10.0'} @@ -3327,6 +3438,9 @@ packages: resolution: {integrity: sha512-XGuRDNjXUijsUL0vl6nSD7cwURuzEgglbOaFuZM9g3kwDXOWVTck0jLzjPzGD+TazWbboZYu52/9/XPdUgne9g==} engines: {node: '>= 0.8.0'} + set-blocking@2.0.0: + resolution: {integrity: sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==} + set-function-length@1.2.2: resolution: {integrity: sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==} engines: {node: '>= 0.4'} @@ -3531,6 +3645,10 @@ packages: resolution: {integrity: sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ==} engines: {node: '>=6'} + tar@6.2.1: + resolution: {integrity: sha512-DZ4yORTwrbTj/7MZYq2w+/ZFdI6OZ/f9SFHR+71gIVUZhOQPHzVCLpvRnPgyaMpfWxxk/4ONva3GQSyNIKRv6A==} + engines: {node: '>=10'} + terser-webpack-plugin@5.3.10: resolution: {integrity: sha512-BKFPWlPDndPs+NGGCr1U59t0XScL5317Y0UReNrHaw9/FwhPENlq6bfgs+4yPfyP51vqC1bQ4rp1EfXW5ZSH9w==} engines: {node: '>= 10.13.0'} @@ -3876,6 +3994,9 @@ packages: engines: {node: '>= 8'} hasBin: true + wide-align@1.1.5: + resolution: {integrity: sha512-eDMORYaPNZ4sQIuuYPDHdQvf4gyCF9rEEV/yPxGfwPkRodwEgiMUUXTx/dex+Me0wxx53S+NgUHaP7y3MGlDmg==} + word-wrap@1.2.5: resolution: {integrity: sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==} engines: {node: '>=0.10.0'} @@ -3910,6 +4031,9 @@ packages: yallist@3.1.1: resolution: {integrity: sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==} + yallist@4.0.0: + resolution: {integrity: sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==} + yaml@2.5.1: resolution: {integrity: sha512-bLQOjaX/ADgQ20isPJRvF0iRUHIxVhYvr53Of7wGcWlO2jvtUlH5m87DsmulFVxRpNLOnI4tB6p/oh8D7kpn9Q==} engines: {node: '>= 14'} @@ -4440,6 +4564,21 @@ snapshots: '@lukeed/csprng@1.1.0': {} + '@mapbox/node-pre-gyp@1.0.11': + dependencies: + detect-libc: 2.0.3 + https-proxy-agent: 5.0.1 + make-dir: 3.1.0 + node-fetch: 2.7.0 + nopt: 5.0.0 + npmlog: 5.0.1 + rimraf: 3.0.2 + semver: 7.6.3 + tar: 6.2.1 + transitivePeerDependencies: + - encoding + - supports-color + '@nestjs/cli@10.4.5': dependencies: '@angular-devkit/core': 17.3.8(chokidar@3.6.0) @@ -4674,6 +4813,10 @@ snapshots: dependencies: '@babel/types': 7.25.6 + '@types/bcrypt@5.0.2': + dependencies: + '@types/node': 20.16.5 + '@types/body-parser@1.19.5': dependencies: '@types/connect': 3.4.38 @@ -4730,6 +4873,8 @@ snapshots: dependencies: '@types/node': 20.16.5 + '@types/lodash@4.17.7': {} + '@types/methods@1.1.4': {} '@types/mime@1.3.5': {} @@ -5055,6 +5200,8 @@ snapshots: '@xtuc/long@4.2.2': {} + abbrev@1.1.1: {} + accepts@1.3.8: dependencies: mime-types: 2.1.35 @@ -5074,6 +5221,12 @@ snapshots: acorn@8.12.1: {} + agent-base@6.0.2: + dependencies: + debug: 4.3.6 + transitivePeerDependencies: + - supports-color + ajv-formats@2.1.1(ajv@8.12.0): optionalDependencies: ajv: 8.12.0 @@ -5129,6 +5282,13 @@ snapshots: append-field@1.0.0: {} + aproba@2.0.0: {} + + are-we-there-yet@2.0.0: + dependencies: + delegates: 1.0.0 + readable-stream: 3.6.2 + arg@4.1.3: {} arg@5.0.2: {} @@ -5289,6 +5449,14 @@ snapshots: base64-js@1.5.1: {} + bcrypt@5.1.1: + dependencies: + '@mapbox/node-pre-gyp': 1.0.11 + node-addon-api: 5.1.0 + transitivePeerDependencies: + - encoding + - supports-color + binary-extensions@2.3.0: {} bl@4.1.0: @@ -5409,6 +5577,8 @@ snapshots: optionalDependencies: fsevents: 2.3.3 + chownr@2.0.0: {} + chrome-trace-event@1.0.4: {} ci-info@3.9.0: {} @@ -5474,6 +5644,8 @@ snapshots: color-name@1.1.4: {} + color-support@1.1.3: {} + combined-stream@1.0.8: dependencies: delayed-stream: 1.0.0 @@ -5503,6 +5675,8 @@ snapshots: consola@2.15.3: {} + console-control-strings@1.1.0: {} + content-disposition@0.5.4: dependencies: safe-buffer: 5.2.1 @@ -5639,10 +5813,14 @@ snapshots: delayed-stream@1.0.0: {} + delegates@1.0.0: {} + depd@2.0.0: {} destroy@1.2.0: {} + detect-libc@2.0.3: {} + detect-newline@3.1.0: {} dezalgo@1.0.4: @@ -6231,6 +6409,10 @@ snapshots: jsonfile: 6.1.0 universalify: 2.0.1 + fs-minipass@2.1.0: + dependencies: + minipass: 3.3.6 + fs-monkey@1.0.6: {} fs.realpath@1.0.0: {} @@ -6249,6 +6431,18 @@ snapshots: functions-have-names@1.2.3: {} + gauge@3.0.2: + dependencies: + aproba: 2.0.0 + color-support: 1.1.3 + console-control-strings: 1.1.0 + has-unicode: 2.0.1 + object-assign: 4.1.1 + signal-exit: 3.0.7 + string-width: 4.2.3 + strip-ansi: 6.0.1 + wide-align: 1.1.5 + gensync@1.0.0-beta.2: {} get-caller-file@2.0.5: {} @@ -6368,6 +6562,8 @@ snapshots: dependencies: has-symbols: 1.0.3 + has-unicode@2.0.1: {} + hasown@2.0.2: dependencies: function-bind: 1.1.2 @@ -6386,6 +6582,13 @@ snapshots: statuses: 2.0.1 toidentifier: 1.0.1 + https-proxy-agent@5.0.1: + dependencies: + agent-base: 6.0.2 + debug: 4.3.6 + transitivePeerDependencies: + - supports-color + human-signals@2.1.0: {} iconv-lite@0.4.24: @@ -7113,6 +7316,10 @@ snapshots: dependencies: '@jridgewell/sourcemap-codec': 1.5.0 + make-dir@3.1.0: + dependencies: + semver: 6.3.1 + make-dir@4.0.0: dependencies: semver: 7.6.3 @@ -7172,12 +7379,25 @@ snapshots: minimist@1.2.8: {} + minipass@3.3.6: + dependencies: + yallist: 4.0.0 + + minipass@5.0.0: {} + minipass@7.1.2: {} + minizlib@2.1.2: + dependencies: + minipass: 3.3.6 + yallist: 4.0.0 + mkdirp@0.5.6: dependencies: minimist: 1.2.8 + mkdirp@1.0.4: {} + mkdirp@2.1.6: {} ms@2.0.0: {} @@ -7241,6 +7461,8 @@ snapshots: node-abort-controller@3.1.1: {} + node-addon-api@5.1.0: {} + node-emoji@1.11.0: dependencies: lodash: 4.17.21 @@ -7253,12 +7475,23 @@ snapshots: node-releases@2.0.18: {} + nopt@5.0.0: + dependencies: + abbrev: 1.1.1 + normalize-path@3.0.0: {} npm-run-path@4.0.1: dependencies: path-key: 3.1.1 + npmlog@5.0.1: + dependencies: + are-we-there-yet: 2.0.0 + console-control-strings: 1.1.0 + gauge: 3.0.2 + set-blocking: 2.0.0 + object-assign@4.1.1: {} object-hash@3.0.0: {} @@ -7758,6 +7991,8 @@ snapshots: transitivePeerDependencies: - supports-color + set-blocking@2.0.0: {} + set-function-length@1.2.2: dependencies: define-data-property: 1.1.4 @@ -8005,6 +8240,15 @@ snapshots: tapable@2.2.1: {} + tar@6.2.1: + dependencies: + chownr: 2.0.0 + fs-minipass: 2.1.0 + minipass: 5.0.0 + minizlib: 2.1.2 + mkdirp: 1.0.4 + yallist: 4.0.0 + terser-webpack-plugin@5.3.10(webpack@5.94.0): dependencies: '@jridgewell/trace-mapping': 0.3.25 @@ -8348,6 +8592,10 @@ snapshots: dependencies: isexe: 2.0.0 + wide-align@1.1.5: + dependencies: + string-width: 4.2.3 + word-wrap@1.2.5: {} wrap-ansi@6.2.0: @@ -8381,6 +8629,8 @@ snapshots: yallist@3.1.1: {} + yallist@4.0.0: {} + yaml@2.5.1: {} yargs-parser@20.2.9: {} diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml index 47c110cc..55121be7 100644 --- a/pnpm-workspace.yaml +++ b/pnpm-workspace.yaml @@ -5,7 +5,8 @@ packages: catalog: - pg: "^8.12.0" - typeorm: "^0.3.20" - zod: "^3.23.8" - class-transformer: "^0.5.1" \ No newline at end of file + pg: "8.12.0" + typeorm: "0.3.20" + zod: "3.23.8" + class-transformer: "0.5.1" + bcrypt: "5.1.1" \ No newline at end of file diff --git a/tsconfig.json b/tsconfig.json index a6b76c5d..0ab4213e 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -14,7 +14,6 @@ "@api/*": ["./api/src/*"], "@client/*": ["./client/*"], "@shared/*": ["./shared/*"], - "@auth/*": ["./api/src/modules/auth/*"], } } } \ No newline at end of file