diff --git a/apps/auth/src/config/app.config.ts b/apps/auth/src/config/app.config.ts new file mode 100644 index 0000000..ef1ad58 --- /dev/null +++ b/apps/auth/src/config/app.config.ts @@ -0,0 +1,8 @@ +export default () => ({ + DB_HOST: process.env.DB_HOST, + DB_PORT: process.env.DB_PORT, + DB_USER: process.env.DB_USER, + DB_PASSWORD: process.env.DB_PASSWORD, + DB_NAME: process.env.DB_NAME, + DB_SYNC: process.env.DB_SYNC, +}); diff --git a/apps/auth/src/config/index.ts b/apps/auth/src/config/index.ts index adf5667..f1ccc51 100644 --- a/apps/auth/src/config/index.ts +++ b/apps/auth/src/config/index.ts @@ -1,3 +1,4 @@ import AuthConfig from './auth.config'; +import AppConfig from './app.config' -export default [AuthConfig]; +export default [AuthConfig,AppConfig]; diff --git a/apps/auth/src/config/jwt.config.ts b/apps/auth/src/config/jwt.config.ts new file mode 100644 index 0000000..17a1e92 --- /dev/null +++ b/apps/auth/src/config/jwt.config.ts @@ -0,0 +1,11 @@ +import { registerAs } from '@nestjs/config'; + +export default registerAs( + 'jwt', + (): Record => ({ + secret: process.env.JWT_SECRET, + expire_time: process.env.JWT_EXPIRE_TIME, + secret_refresh: process.env.JWT_SECRET_REFRESH, + expire_refresh: process.env.JWT_EXPIRE_TIME_REFRESH, + }) +); \ No newline at end of file diff --git a/apps/auth/src/main.ts b/apps/auth/src/main.ts index 9572bdb..7d68bcc 100644 --- a/apps/auth/src/main.ts +++ b/apps/auth/src/main.ts @@ -1,8 +1,32 @@ import { NestFactory } from '@nestjs/core'; import { AuthModule } from './auth.module'; +import rateLimit from 'express-rate-limit'; +import helmet from 'helmet'; +import { setupSwaggerAuthentication } from '@app/common/util/user-auth.swagger.utils'; +import { ValidationPipe } from '@nestjs/common'; async function bootstrap() { const app = await NestFactory.create(AuthModule); + + app.enableCors(); + + app.use( + rateLimit({ + windowMs: 5 * 60 * 1000, + max: 500, + }), + ); + + app.use( + helmet({ + contentSecurityPolicy: false, + }), + ); + + setupSwaggerAuthentication(app); + + app.useGlobalPipes(new ValidationPipe()); + await app.listen(6001); } bootstrap(); diff --git a/apps/auth/src/modules/authentication/authentication.module.ts b/apps/auth/src/modules/authentication/authentication.module.ts index eb5c84c..11069eb 100644 --- a/apps/auth/src/modules/authentication/authentication.module.ts +++ b/apps/auth/src/modules/authentication/authentication.module.ts @@ -2,11 +2,24 @@ import { Module } from '@nestjs/common'; import { AuthenticationController } from './controllers/authentication.controller'; import { AuthenticationService } from './services/authentication.service'; import { ConfigModule } from '@nestjs/config'; +import { UserRepositoryModule } from '@app/common/modules/user/user.repository.module'; +import { CommonModule } from '@app/common'; +import { UserAuthController } from './controllers'; +import { UserAuthService } from './services'; +import { UserRepository } from '@app/common/modules/user/repositories'; +import { UserSessionRepository } from '@app/common/modules/session/repositories/session.repository'; +import { UserOtpRepository } from '@app/common/modules/user-otp/repositories/user-otp.repository'; @Module({ - imports: [ConfigModule], - controllers: [AuthenticationController], - providers: [AuthenticationService], - exports: [AuthenticationService], + imports: [ConfigModule, UserRepositoryModule, CommonModule], + controllers: [AuthenticationController, UserAuthController], + providers: [ + AuthenticationService, + UserAuthService, + UserRepository, + UserSessionRepository, + UserOtpRepository, + ], + exports: [AuthenticationService, UserAuthService], }) export class AuthenticationModule {} diff --git a/apps/auth/src/modules/authentication/constants/login.response.constant.ts b/apps/auth/src/modules/authentication/constants/login.response.constant.ts new file mode 100644 index 0000000..0be9517 --- /dev/null +++ b/apps/auth/src/modules/authentication/constants/login.response.constant.ts @@ -0,0 +1,3 @@ +export interface ILoginResponse { + access_token: string; +} diff --git a/apps/auth/src/modules/authentication/controllers/authentication.controller.ts b/apps/auth/src/modules/authentication/controllers/authentication.controller.ts index 931a522..070620b 100644 --- a/apps/auth/src/modules/authentication/controllers/authentication.controller.ts +++ b/apps/auth/src/modules/authentication/controllers/authentication.controller.ts @@ -1,10 +1,12 @@ import { Controller, Post } from '@nestjs/common'; import { AuthenticationService } from '../services/authentication.service'; +import { ApiTags } from '@nestjs/swagger'; @Controller({ version: '1', path: 'authentication', }) +@ApiTags('Tuya Auth') export class AuthenticationController { constructor(private readonly authenticationService: AuthenticationService) {} @Post('auth') diff --git a/apps/auth/src/modules/authentication/controllers/index.ts b/apps/auth/src/modules/authentication/controllers/index.ts new file mode 100644 index 0000000..2ce466d --- /dev/null +++ b/apps/auth/src/modules/authentication/controllers/index.ts @@ -0,0 +1,2 @@ +export * from './authentication.controller'; +export * from './user-auth.controller'; diff --git a/apps/auth/src/modules/authentication/controllers/user-auth.controller.ts b/apps/auth/src/modules/authentication/controllers/user-auth.controller.ts new file mode 100644 index 0000000..21f9f66 --- /dev/null +++ b/apps/auth/src/modules/authentication/controllers/user-auth.controller.ts @@ -0,0 +1,95 @@ +import { + Body, + Controller, + Delete, + HttpStatus, + Param, + Post, + UseGuards, +} from '@nestjs/common'; +import { UserAuthService } from '../services/user-auth.service'; +import { UserSignUpDto } from '../dtos/user-auth.dto'; +import { ApiBearerAuth, ApiTags } from '@nestjs/swagger'; +import { ResponseMessage } from '@app/common/response/response.decorator'; +import { UserLoginDto } from '../dtos/user-login.dto'; +import { JwtAuthGuard } from '@app/common/guards/jwt.auth.guard'; +import { ForgetPasswordDto, UserOtpDto, VerifyOtpDto } from '../dtos'; + +@Controller({ + version: '1', + path: 'authentication', +}) +@ApiTags('Auth') +export class UserAuthController { + constructor(private readonly userAuthService: UserAuthService) {} + + @ResponseMessage('User Registered Successfully') + @Post('user/signup') + async signUp(@Body() userSignUpDto: UserSignUpDto) { + const signupUser = await this.userAuthService.signUp(userSignUpDto); + return { + statusCode: HttpStatus.CREATED, + data: { + id: signupUser.uuid, + }, + message: 'User Registered Successfully', + }; + } + + @ResponseMessage('user logged in successfully') + @Post('user/login') + async userLogin(@Body() data: UserLoginDto) { + const accessToken = await this.userAuthService.userLogin(data); + return { + statusCode: HttpStatus.CREATED, + data: accessToken, + message: 'User Loggedin Successfully', + }; + } + + @ApiBearerAuth() + @UseGuards(JwtAuthGuard) + @Delete('user/delete/:id') + async userDelete(@Param('id') id: string) { + await this.userAuthService.deleteUser(id); + return { + statusCode: HttpStatus.OK, + data: { + id, + }, + message: 'User Deleted Successfully', + }; + } + + @Post('user/send-otp') + async sendOtp(@Body() otpDto: UserOtpDto) { + const otpCode = await this.userAuthService.generateOTP(otpDto); + return { + statusCode: HttpStatus.OK, + data: { + otp: otpCode, + }, + message: 'Otp Send Successfully', + }; + } + + @Post('user/verify-otp') + async verifyOtp(@Body() verifyOtpDto: VerifyOtpDto) { + await this.userAuthService.verifyOTP(verifyOtpDto); + return { + statusCode: HttpStatus.OK, + data: {}, + message: 'Otp Verified Successfully', + }; + } + + @Post('user/forget-password') + async forgetPassword(@Body() forgetPasswordDto: ForgetPasswordDto) { + await this.userAuthService.forgetPassword(forgetPasswordDto); + return { + statusCode: HttpStatus.OK, + data: {}, + message: 'Password changed successfully', + }; + } +} diff --git a/apps/auth/src/modules/authentication/dtos/index.ts b/apps/auth/src/modules/authentication/dtos/index.ts new file mode 100644 index 0000000..069bcc1 --- /dev/null +++ b/apps/auth/src/modules/authentication/dtos/index.ts @@ -0,0 +1,4 @@ +export * from './user-auth.dto' +export * from './user-login.dto' +export * from './user-otp.dto' +export * from './user-password.dto' \ No newline at end of file diff --git a/apps/auth/src/modules/authentication/dtos/user-auth.dto.ts b/apps/auth/src/modules/authentication/dtos/user-auth.dto.ts new file mode 100644 index 0000000..8e77057 --- /dev/null +++ b/apps/auth/src/modules/authentication/dtos/user-auth.dto.ts @@ -0,0 +1,36 @@ +import { ApiProperty } from '@nestjs/swagger'; +import { IsEmail, IsNotEmpty, IsString } from 'class-validator'; + +export class UserSignUpDto { + @ApiProperty({ + description:'email', + required:true + }) + @IsEmail() + @IsNotEmpty() + public email: string; + + @ApiProperty({ + description:'password', + required:true + }) + @IsString() + @IsNotEmpty() + public password: string; + + @ApiProperty({ + description:'first name', + required:true + }) + @IsString() + @IsNotEmpty() + public firstName: string; + + @ApiProperty({ + description:'last name', + required:true + }) + @IsString() + @IsNotEmpty() + public lastName: string; +} diff --git a/apps/auth/src/modules/authentication/dtos/user-login.dto.ts b/apps/auth/src/modules/authentication/dtos/user-login.dto.ts new file mode 100644 index 0000000..6a14047 --- /dev/null +++ b/apps/auth/src/modules/authentication/dtos/user-login.dto.ts @@ -0,0 +1,14 @@ +import { ApiProperty } from '@nestjs/swagger'; +import { IsEmail, IsNotEmpty, IsOptional, IsString } from 'class-validator'; + +export class UserLoginDto { + @ApiProperty() + @IsEmail() + @IsNotEmpty() + email: string; + + @ApiProperty() + @IsString() + @IsOptional() + password: string; +} diff --git a/apps/auth/src/modules/authentication/dtos/user-otp.dto.ts b/apps/auth/src/modules/authentication/dtos/user-otp.dto.ts new file mode 100644 index 0000000..d4fad12 --- /dev/null +++ b/apps/auth/src/modules/authentication/dtos/user-otp.dto.ts @@ -0,0 +1,22 @@ +import { OtpType } from '@app/common/constants/otp-type.enum'; +import { ApiProperty } from '@nestjs/swagger'; +import { IsEmail, IsEnum, IsNotEmpty, IsString } from 'class-validator'; + +export class UserOtpDto { + @ApiProperty() + @IsEmail() + @IsNotEmpty() + email: string; + + @ApiProperty() + @IsEnum(OtpType) + @IsNotEmpty() + type: OtpType; +} + +export class VerifyOtpDto extends UserOtpDto { + @ApiProperty() + @IsString() + @IsNotEmpty() + otpCode: string; +} diff --git a/apps/auth/src/modules/authentication/dtos/user-password.dto.ts b/apps/auth/src/modules/authentication/dtos/user-password.dto.ts new file mode 100644 index 0000000..fe2118c --- /dev/null +++ b/apps/auth/src/modules/authentication/dtos/user-password.dto.ts @@ -0,0 +1,14 @@ +import { ApiProperty } from '@nestjs/swagger'; +import { IsEmail, IsNotEmpty, IsString } from 'class-validator'; + +export class ForgetPasswordDto { + @ApiProperty() + @IsEmail() + @IsNotEmpty() + email: string; + + @ApiProperty() + @IsString() + @IsNotEmpty() + password: string; +} diff --git a/apps/auth/src/modules/authentication/services/index.ts b/apps/auth/src/modules/authentication/services/index.ts new file mode 100644 index 0000000..ac532d6 --- /dev/null +++ b/apps/auth/src/modules/authentication/services/index.ts @@ -0,0 +1,2 @@ +export * from './authentication.service'; +export * from './user-auth.service'; diff --git a/apps/auth/src/modules/authentication/services/user-auth.service.ts b/apps/auth/src/modules/authentication/services/user-auth.service.ts new file mode 100644 index 0000000..4f75152 --- /dev/null +++ b/apps/auth/src/modules/authentication/services/user-auth.service.ts @@ -0,0 +1,151 @@ +import { UserRepository } from '@app/common/modules/user/repositories'; +import { + BadRequestException, + Injectable, + UnauthorizedException, +} from '@nestjs/common'; +import { UserSignUpDto } from '../dtos/user-auth.dto'; +import { HelperHashService } from '@app/common/helper/services'; +import { UserLoginDto } from '../dtos/user-login.dto'; +import { AuthService } from '@app/common/auth/services/auth.service'; +import { UserSessionRepository } from '@app/common/modules/session/repositories/session.repository'; +import { UserOtpRepository } from '@app/common/modules/user-otp/repositories/user-otp.repository'; +import { ForgetPasswordDto, UserOtpDto, VerifyOtpDto } from '../dtos'; +import { EmailService } from '@app/common/util/email.service'; +import { OtpType } from '@app/common/constants/otp-type.enum'; +import { UserEntity } from '@app/common/modules/user/entities/user.entity'; +import { ILoginResponse } from '../constants/login.response.constant'; + +@Injectable() +export class UserAuthService { + constructor( + private readonly userRepository: UserRepository, + private readonly sessionRepository: UserSessionRepository, + private readonly otpRepository: UserOtpRepository, + private readonly helperHashService: HelperHashService, + private readonly authService: AuthService, + private readonly emailService: EmailService, + ) {} + + async signUp(userSignUpDto: UserSignUpDto): Promise { + const findUser = await this.findUser(userSignUpDto.email); + if (findUser) { + throw new BadRequestException('User already registered with given email'); + } + const salt = this.helperHashService.randomSalt(10); + const password = this.helperHashService.bcrypt( + userSignUpDto.password, + salt, + ); + return await this.userRepository.save({ ...userSignUpDto, password }); + } + + async findUser(email: string) { + return await this.userRepository.findOne({ + where: { + email, + }, + }); + } + + async forgetPassword(forgetPasswordDto: ForgetPasswordDto) { + const findUser = await this.findUser(forgetPasswordDto.email); + if (!findUser) { + throw new BadRequestException('User not found'); + } + const salt = this.helperHashService.randomSalt(10); + const password = this.helperHashService.bcrypt( + forgetPasswordDto.password, + salt, + ); + return await this.userRepository.update( + { uuid: findUser.uuid }, + { password }, + ); + } + + async userLogin(data: UserLoginDto): Promise { + const user = await this.authService.validateUser(data.email, data.password); + if (!user) { + throw new UnauthorizedException('Invalid login credentials.'); + } + + const session = await Promise.all([ + await this.sessionRepository.update( + { userId: user.id }, + { + isLoggedOut: true, + }, + ), + await this.authService.createSession({ + userId: user.uuid, + loginTime: new Date(), + isLoggedOut: false, + }), + ]); + + return await this.authService.login({ + email: user.email, + userId: user.id, + uuid: user.uuid, + sessionId: session[1].uuid, + }); + } + + async deleteUser(uuid: string) { + const user = await this.findOneById(uuid); + if (!user) { + throw new BadRequestException('User does not found'); + } + return await this.userRepository.delete({ uuid }); + } + + async findOneById(id: string): Promise { + return await this.userRepository.findOne({ where: { uuid: id } }); + } + + async generateOTP(data: UserOtpDto): Promise { + await this.otpRepository.delete({ email: data.email, type: data.type }); + const otpCode = Math.floor(100000 + Math.random() * 900000).toString(); + const expiryTime = new Date(); + expiryTime.setMinutes(expiryTime.getMinutes() + 1); + await this.otpRepository.save({ + email: data.email, + otpCode, + expiryTime, + type: data.type, + }); + const subject = 'OTP send successfully'; + const message = `Your OTP code is ${otpCode}`; + this.emailService.sendOTPEmail(data.email, subject, message); + return otpCode; + } + + async verifyOTP(data: VerifyOtpDto): Promise { + const otp = await this.otpRepository.findOne({ + where: { email: data.email, type: data.type }, + }); + + if (!otp) { + throw new BadRequestException('this email is not registered'); + } + + if (otp.otpCode !== data.otpCode) { + throw new BadRequestException('You entered wrong otp'); + } + + if (otp.expiryTime < new Date()) { + await this.otpRepository.delete(otp.id); + throw new BadRequestException('OTP expired'); + } + + if (data.type == OtpType.VERIFICATION) { + await this.userRepository.update( + { email: data.email }, + { isUserVerified: true }, + ); + } + + return true; + } +} diff --git a/apps/backend/src/backend.module.ts b/apps/backend/src/backend.module.ts index c41a9b8..9afbd2f 100644 --- a/apps/backend/src/backend.module.ts +++ b/apps/backend/src/backend.module.ts @@ -4,6 +4,7 @@ import { AppService } from './backend.service'; import { UserModule } from './modules/user/user.module'; import { ConfigModule } from '@nestjs/config'; import config from './config'; +import { CommonModule } from '@app/common'; @Module({ imports: [ @@ -11,6 +12,7 @@ import config from './config'; load: config, }), UserModule, + CommonModule, ], controllers: [AppController], providers: [AppService], diff --git a/apps/backend/src/config/app.config.ts b/apps/backend/src/config/app.config.ts new file mode 100644 index 0000000..ef1ad58 --- /dev/null +++ b/apps/backend/src/config/app.config.ts @@ -0,0 +1,8 @@ +export default () => ({ + DB_HOST: process.env.DB_HOST, + DB_PORT: process.env.DB_PORT, + DB_USER: process.env.DB_USER, + DB_PASSWORD: process.env.DB_PASSWORD, + DB_NAME: process.env.DB_NAME, + DB_SYNC: process.env.DB_SYNC, +}); diff --git a/apps/backend/src/config/index.ts b/apps/backend/src/config/index.ts index adf5667..f8720ad 100644 --- a/apps/backend/src/config/index.ts +++ b/apps/backend/src/config/index.ts @@ -1,3 +1,4 @@ import AuthConfig from './auth.config'; - -export default [AuthConfig]; +import AppConfig from './app.config' +import JwtConfig from './jwt.config' +export default [AuthConfig,AppConfig,JwtConfig]; diff --git a/apps/backend/src/config/jwt.config.ts b/apps/backend/src/config/jwt.config.ts new file mode 100644 index 0000000..a4ff896 --- /dev/null +++ b/apps/backend/src/config/jwt.config.ts @@ -0,0 +1,11 @@ +import { registerAs } from '@nestjs/config'; + +export default registerAs( + 'jwt', + (): Record => ({ + secret: process.env.JWT_SECRET, + expire_time: process.env.JWT_EXPIRE_TIME, + secret_refresh: process.env.JWT_SECRET_REFRESH, + expire_refresh: process.env.JWT_EXPIRE_TIME_REFRESH, + }) +); diff --git a/apps/backend/src/main.ts b/apps/backend/src/main.ts index 9343e74..c062492 100644 --- a/apps/backend/src/main.ts +++ b/apps/backend/src/main.ts @@ -1,8 +1,26 @@ import { NestFactory } from '@nestjs/core'; import { BackendModule } from './backend.module'; +import rateLimit from 'express-rate-limit'; +import helmet from 'helmet'; +import { ValidationPipe } from '@nestjs/common'; async function bootstrap() { const app = await NestFactory.create(BackendModule); + app.enableCors(); + + app.use( + rateLimit({ + windowMs: 5 * 60 * 1000, // 5 minutes + max: 500, // limit each IP to 500 requests per windowMs + }), + ); + + app.use( + helmet({ + contentSecurityPolicy: false, + }), + ); + app.useGlobalPipes(new ValidationPipe()); await app.listen(6000); } bootstrap(); diff --git a/apps/backend/src/modules/user/controllers/index.ts b/apps/backend/src/modules/user/controllers/index.ts new file mode 100644 index 0000000..bb9c2b0 --- /dev/null +++ b/apps/backend/src/modules/user/controllers/index.ts @@ -0,0 +1 @@ +export * from './user.controller' \ No newline at end of file diff --git a/apps/backend/src/modules/user/controllers/user.controller.ts b/apps/backend/src/modules/user/controllers/user.controller.ts index 901506a..705a748 100644 --- a/apps/backend/src/modules/user/controllers/user.controller.ts +++ b/apps/backend/src/modules/user/controllers/user.controller.ts @@ -1,8 +1,9 @@ import { Controller, Get, Query } from '@nestjs/common'; import { UserService } from '../services/user.service'; import { UserListDto } from '../dtos/user.list.dto'; +import { ApiTags } from '@nestjs/swagger'; -//@ApiTags('User Module') +@ApiTags('User Module') @Controller({ version: '1', path: 'user', diff --git a/apps/backend/src/modules/user/dtos/index.ts b/apps/backend/src/modules/user/dtos/index.ts new file mode 100644 index 0000000..ad0550f --- /dev/null +++ b/apps/backend/src/modules/user/dtos/index.ts @@ -0,0 +1 @@ +export * from './user.list.dto' \ No newline at end of file diff --git a/apps/backend/src/modules/user/services/index.ts b/apps/backend/src/modules/user/services/index.ts new file mode 100644 index 0000000..5a6a9db --- /dev/null +++ b/apps/backend/src/modules/user/services/index.ts @@ -0,0 +1 @@ +export * from './user.service' \ No newline at end of file diff --git a/libs/common/src/auth/auth.module.ts b/libs/common/src/auth/auth.module.ts new file mode 100644 index 0000000..64de838 --- /dev/null +++ b/libs/common/src/auth/auth.module.ts @@ -0,0 +1,28 @@ +import { PassportModule } from '@nestjs/passport'; +import { JwtModule } from '@nestjs/jwt'; +import { ConfigModule, ConfigService } from '@nestjs/config'; +import { Module } from '@nestjs/common'; +import { HelperModule } from '../helper/helper.module'; +import { JwtStrategy } from './strategies/jwt.strategy'; +import { UserSessionRepository } from '../modules/session/repositories/session.repository'; +import { AuthService } from './services/auth.service'; +import { UserRepository } from '../modules/user/repositories'; + +@Module({ + imports: [ + ConfigModule.forRoot(), + PassportModule, + JwtModule.registerAsync({ + imports: [ConfigModule], + inject: [ConfigService], + useFactory: async (configService: ConfigService) => ({ + secret: configService.get('JWT_SECRET'), + signOptions: { expiresIn: configService.get('JWT_EXPIRE_TIME') }, + }), + }), + HelperModule, + ], + providers: [JwtStrategy, UserSessionRepository,AuthService,UserRepository], + exports: [AuthService], +}) +export class AuthModule {} diff --git a/libs/common/src/auth/interfaces/auth.interface.ts b/libs/common/src/auth/interfaces/auth.interface.ts new file mode 100644 index 0000000..ae85fe1 --- /dev/null +++ b/libs/common/src/auth/interfaces/auth.interface.ts @@ -0,0 +1,7 @@ +export class AuthInterface { + email: string; + userId: number; + uuid: string; + sessionId: string; + id: number; +} diff --git a/libs/common/src/auth/services/auth.service.ts b/libs/common/src/auth/services/auth.service.ts new file mode 100644 index 0000000..2d3c0ee --- /dev/null +++ b/libs/common/src/auth/services/auth.service.ts @@ -0,0 +1,55 @@ +import { BadRequestException, Injectable } from '@nestjs/common'; +import { JwtService } from '@nestjs/jwt'; +import { HelperHashService } from '../../helper/services'; +import { UserRepository } from '@app/common/modules/user/repositories'; +import { UserSessionRepository } from '@app/common/modules/session/repositories/session.repository'; +import { UserSessionEntity } from '@app/common/modules/session/entities'; + +@Injectable() +export class AuthService { + constructor( + private jwtService: JwtService, + private readonly userRepository: UserRepository, + private readonly sessionRepository: UserSessionRepository, + private readonly helperHashService: HelperHashService, + ) {} + + async validateUser(email: string, pass: string): Promise { + const user = await this.userRepository.findOne({ + where: { + email, + }, + }); + if (!user.isUserVerified) { + throw new BadRequestException('User is not verified'); + } + if (user) { + const passwordMatch = this.helperHashService.bcryptCompare( + pass, + user.password, + ); + if (passwordMatch) { + const { ...result } = user; + return result; + } + } + return null; + } + + async createSession(data): Promise { + return await this.sessionRepository.save(data); + } + + async login(user: any) { + const payload = { + email: user.email, + userId: user.userId, + uuid: user.uuid, + type: user.type, + sessionId: user.sessionId, + }; + return { + access_token: this.jwtService.sign(payload), + }; + } +} diff --git a/libs/common/src/auth/strategies/jwt.strategy.ts b/libs/common/src/auth/strategies/jwt.strategy.ts new file mode 100644 index 0000000..833f42c --- /dev/null +++ b/libs/common/src/auth/strategies/jwt.strategy.ts @@ -0,0 +1,39 @@ +import { ConfigService } from '@nestjs/config'; +import { ExtractJwt, Strategy } from 'passport-jwt'; +import { PassportStrategy } from '@nestjs/passport'; +import { BadRequestException, Injectable } from '@nestjs/common'; +import { UserSessionRepository } from '@app/common/modules/session/repositories/session.repository'; +import { AuthInterface } from '../interfaces/auth.interface'; + +@Injectable() +export class JwtStrategy extends PassportStrategy(Strategy) { + constructor( + private readonly sessionRepository: UserSessionRepository, + private readonly configService: ConfigService, + ) { + super({ + jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(), + ignoreExpiration: false, + secretOrKey: configService.get('JWT_SECRET'), + }); + } + + async validate(payload: AuthInterface) { + const validateUser = await this.sessionRepository.findOne({ + where: { + uuid: payload.sessionId, + isLoggedOut: false, + }, + }); + if (validateUser) { + return { + email: payload.email, + userId: payload.id, + uuid: payload.uuid, + sessionId: payload.sessionId, + }; + } else { + throw new BadRequestException('Unauthorized'); + } + } +} diff --git a/libs/common/src/common.module.ts b/libs/common/src/common.module.ts new file mode 100644 index 0000000..62ea3a4 --- /dev/null +++ b/libs/common/src/common.module.ts @@ -0,0 +1,22 @@ +import { Module } from '@nestjs/common'; +import { CommonService } from './common.service'; +import { DatabaseModule } from './database/database.module'; +import { HelperModule } from './helper/helper.module'; +import { AuthModule } from './auth/auth.module'; +import { ConfigModule } from '@nestjs/config'; +import config from './config'; +import { EmailService } from './util/email.service'; + +@Module({ + providers: [CommonService,EmailService], + exports: [CommonService, HelperModule, AuthModule,EmailService], + imports: [ + ConfigModule.forRoot({ + load: config, + }), + DatabaseModule, + HelperModule, + AuthModule, + ], +}) +export class CommonModule {} diff --git a/libs/common/src/common.service.spec.ts b/libs/common/src/common.service.spec.ts new file mode 100644 index 0000000..6ea5a77 --- /dev/null +++ b/libs/common/src/common.service.spec.ts @@ -0,0 +1,18 @@ +import { Test, TestingModule } from '@nestjs/testing'; +import { CommonService } from './common.service'; + +describe('CommonService', () => { + let service: CommonService; + + beforeEach(async () => { + const module: TestingModule = await Test.createTestingModule({ + providers: [CommonService], + }).compile(); + + service = module.get(CommonService); + }); + + it('should be defined', () => { + expect(service).toBeDefined(); + }); +}); diff --git a/libs/common/src/common.service.ts b/libs/common/src/common.service.ts new file mode 100644 index 0000000..f0369a5 --- /dev/null +++ b/libs/common/src/common.service.ts @@ -0,0 +1,4 @@ +import { Injectable } from '@nestjs/common'; + +@Injectable() +export class CommonService {} diff --git a/libs/common/src/config/email.config.ts b/libs/common/src/config/email.config.ts new file mode 100644 index 0000000..4050433 --- /dev/null +++ b/libs/common/src/config/email.config.ts @@ -0,0 +1,9 @@ +import { registerAs } from '@nestjs/config'; + +export default registerAs( + 'email-config', + (): Record => ({ + EMAIL_ID: process.env.EMAIL_USER, + PASSWORD: process.env.EMAIL_PASSWORD, + }), +); diff --git a/libs/common/src/config/index.ts b/libs/common/src/config/index.ts new file mode 100644 index 0000000..94380ce --- /dev/null +++ b/libs/common/src/config/index.ts @@ -0,0 +1,2 @@ +import emailConfig from './email.config'; +export default [emailConfig]; diff --git a/libs/common/src/constants/otp-type.enum.ts b/libs/common/src/constants/otp-type.enum.ts new file mode 100644 index 0000000..918dd63 --- /dev/null +++ b/libs/common/src/constants/otp-type.enum.ts @@ -0,0 +1,4 @@ +export enum OtpType { + VERIFICATION = 'VERIFICATION', + PASSWORD = 'PASSWORD', +} diff --git a/libs/common/src/database/database.module.ts b/libs/common/src/database/database.module.ts new file mode 100644 index 0000000..0302f0f --- /dev/null +++ b/libs/common/src/database/database.module.ts @@ -0,0 +1,38 @@ +import { Module } from '@nestjs/common'; +import { ConfigModule, ConfigService } from '@nestjs/config'; +import { TypeOrmModule } from '@nestjs/typeorm'; +import { SnakeNamingStrategy } from './strategies'; +import { UserEntity } from '../modules/user/entities/user.entity'; +import { UserSessionEntity } from '../modules/session/entities/session.entity'; +import { UserOtpEntity } from '../modules/user-otp/entities'; + +@Module({ + imports: [ + TypeOrmModule.forRootAsync({ + imports: [ConfigModule], + inject: [ConfigService], + useFactory: (configService: ConfigService) => ({ + name: 'default', + type: 'postgres', + host: configService.get('DB_HOST'), + port: configService.get('DB_PORT'), + username: configService.get('DB_USER'), + password: configService.get('DB_PASSWORD'), + database: configService.get('DB_NAME'), + entities: [UserEntity, UserSessionEntity,UserOtpEntity], + namingStrategy: new SnakeNamingStrategy(), + synchronize: Boolean(JSON.parse(configService.get('DB_SYNC'))), + logging: true, + extra: { + charset: 'utf8mb4', + max: 20, // set pool max size + idleTimeoutMillis: 5000, // close idle clients after 5 second + connectionTimeoutMillis: 11_000, // return an error after 11 second if connection could not be established + maxUses: 7500, // close (and replace) a connection after it has been used 7500 times (see below for discussion) + }, + continuationLocalStorage: true, + }), + }), + ], +}) +export class DatabaseModule {} diff --git a/libs/common/src/database/strategies/index.ts b/libs/common/src/database/strategies/index.ts new file mode 100644 index 0000000..0dabf18 --- /dev/null +++ b/libs/common/src/database/strategies/index.ts @@ -0,0 +1 @@ +export * from './snack-naming.strategy'; diff --git a/libs/common/src/database/strategies/snack-naming.strategy.ts b/libs/common/src/database/strategies/snack-naming.strategy.ts new file mode 100644 index 0000000..fed7e37 --- /dev/null +++ b/libs/common/src/database/strategies/snack-naming.strategy.ts @@ -0,0 +1,62 @@ +import { DefaultNamingStrategy, NamingStrategyInterface } from 'typeorm'; +import { snakeCase } from 'typeorm/util/StringUtils'; + +export class SnakeNamingStrategy + extends DefaultNamingStrategy + implements NamingStrategyInterface +{ + tableName(className: string, customName: string): string { + return customName ? customName : snakeCase(className); + } + + columnName( + propertyName: string, + customName: string, + embeddedPrefixes: string[], + ): string { + return ( + snakeCase(embeddedPrefixes.join('_')) + + (customName ? customName : snakeCase(propertyName)) + ); + } + + relationName(propertyName: string): string { + return snakeCase(propertyName); + } + + joinColumnName(relationName: string, referencedColumnName: string): string { + return snakeCase(relationName + '_' + referencedColumnName); + } + + joinTableName( + firstTableName: string, + secondTableName: string, + firstPropertyName: any, + _secondPropertyName: string, + ): string { + return snakeCase( + firstTableName + + '_' + + firstPropertyName.replaceAll(/\./gi, '_') + + '_' + + secondTableName, + ); + } + + joinTableColumnName( + tableName: string, + propertyName: string, + columnName?: string, + ): string { + return snakeCase( + tableName + '_' + (columnName ? columnName : propertyName), + ); + } + + classTableInheritanceParentColumnName( + parentTableName: string, + parentTableIdPropertyName: string, + ): string { + return snakeCase(`${parentTableName}_${parentTableIdPropertyName}`); + } +} diff --git a/libs/common/src/guards/jwt.auth.guard.ts b/libs/common/src/guards/jwt.auth.guard.ts new file mode 100644 index 0000000..1b57b77 --- /dev/null +++ b/libs/common/src/guards/jwt.auth.guard.ts @@ -0,0 +1,11 @@ +import { UnauthorizedException } from '@nestjs/common'; +import { AuthGuard } from '@nestjs/passport'; + +export class JwtAuthGuard extends AuthGuard('jwt') { + handleRequest(err, user, info) { + if (err || !user) { + throw err || new UnauthorizedException(); + } + return user; + } +} diff --git a/libs/common/src/helper/helper.module.ts b/libs/common/src/helper/helper.module.ts new file mode 100644 index 0000000..826883a --- /dev/null +++ b/libs/common/src/helper/helper.module.ts @@ -0,0 +1,11 @@ +import { Global, Module } from '@nestjs/common'; +import { HelperHashService } from './services'; + +@Global() +@Module({ + providers: [HelperHashService], + exports: [HelperHashService], + controllers: [], + imports: [], +}) +export class HelperModule {} diff --git a/libs/common/src/helper/services/helper.hash.service.ts b/libs/common/src/helper/services/helper.hash.service.ts new file mode 100644 index 0000000..b4406b9 --- /dev/null +++ b/libs/common/src/helper/services/helper.hash.service.ts @@ -0,0 +1,63 @@ +import { Injectable } from '@nestjs/common'; +import { compareSync, genSaltSync, hashSync } from 'bcryptjs'; +import { AES, enc, mode, pad, SHA256 } from 'crypto-js'; + +@Injectable() +export class HelperHashService { + randomSalt(length: number): string { + return genSaltSync(length); + } + + bcrypt(passwordString: string, salt: string): string { + return hashSync(passwordString, salt); + } + + bcryptCompare(passwordString: string, passwordHashed: string): boolean { + return compareSync(passwordString, passwordHashed); + } + + sha256(string: string): string { + return SHA256(string).toString(enc.Hex); + } + + sha256Compare(hashOne: string, hashTwo: string): boolean { + return hashOne === hashTwo; + } + + // Encryption function + encryptPassword(password, secretKey) { + return AES.encrypt('trx8g6gi', secretKey).toString(); + } + + // Decryption function + decryptPassword(encryptedPassword, secretKey) { + const bytes = AES.decrypt(encryptedPassword, secretKey); + return bytes.toString(enc.Utf8); + } + + aes256Encrypt( + data: string | Record | Record[], + key: string, + iv: string, + ): string { + const cIv = enc.Utf8.parse(iv); + const cipher = AES.encrypt(JSON.stringify(data), enc.Utf8.parse(key), { + mode: mode.CBC, + padding: pad.Pkcs7, + iv: cIv, + }); + + return cipher.toString(); + } + + aes256Decrypt(encrypted: string, key: string, iv: string) { + const cIv = enc.Utf8.parse(iv); + const cipher = AES.decrypt(encrypted, enc.Utf8.parse(key), { + mode: mode.CBC, + padding: pad.Pkcs7, + iv: cIv, + }); + + return cipher.toString(enc.Utf8); + } +} diff --git a/libs/common/src/helper/services/index.ts b/libs/common/src/helper/services/index.ts new file mode 100644 index 0000000..f3f4682 --- /dev/null +++ b/libs/common/src/helper/services/index.ts @@ -0,0 +1 @@ +export * from './helper.hash.service' \ No newline at end of file diff --git a/libs/common/src/index.ts b/libs/common/src/index.ts new file mode 100644 index 0000000..10e9ae0 --- /dev/null +++ b/libs/common/src/index.ts @@ -0,0 +1,2 @@ +export * from './common.module'; +export * from './common.service'; diff --git a/libs/common/src/modules/abstract/dtos/abstract.dto.ts b/libs/common/src/modules/abstract/dtos/abstract.dto.ts new file mode 100644 index 0000000..c6934b0 --- /dev/null +++ b/libs/common/src/modules/abstract/dtos/abstract.dto.ts @@ -0,0 +1,13 @@ +import { ApiProperty } from '@nestjs/swagger'; +import { AbstractEntity } from '../entities/abstract.entity'; + +export class AbstractDto { + @ApiProperty({ format: 'uuid' }) + readonly uuid: string; + + constructor(abstract: AbstractEntity, options?: { excludeFields?: boolean }) { + if (!options?.excludeFields) { + this.uuid = abstract.uuid; + } + } +} diff --git a/libs/common/src/modules/abstract/dtos/index.ts b/libs/common/src/modules/abstract/dtos/index.ts new file mode 100644 index 0000000..fd297f8 --- /dev/null +++ b/libs/common/src/modules/abstract/dtos/index.ts @@ -0,0 +1 @@ +export * from './abstract.dto' \ No newline at end of file diff --git a/libs/common/src/modules/abstract/entities/abstract.entity.ts b/libs/common/src/modules/abstract/entities/abstract.entity.ts new file mode 100644 index 0000000..c065c9b --- /dev/null +++ b/libs/common/src/modules/abstract/entities/abstract.entity.ts @@ -0,0 +1,47 @@ +import { Exclude } from 'class-transformer'; +import { + Column, + CreateDateColumn, + Generated, + PrimaryGeneratedColumn, + UpdateDateColumn, +} from 'typeorm'; + +import { AbstractDto } from '../dtos'; +import { Constructor } from '@app/common/util/types'; + +export abstract class AbstractEntity< + T extends AbstractDto = AbstractDto, + O = never +> { + @PrimaryGeneratedColumn('increment') + @Exclude() + public id: number; + + @Column() + @Generated('uuid') + public uuid: string; + + @CreateDateColumn({ type: 'timestamp' }) + @Exclude() + public createdAt: Date; + + @UpdateDateColumn({ type: 'timestamp' }) + @Exclude() + public updatedAt: Date; + + private dtoClass: Constructor; + + toDto(options?: O): T { + const dtoClass = this.dtoClass; + + // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition + if (!dtoClass) { + throw new Error( + `You need to use @UseDto on class (${this.constructor.name}) be able to call toDto function` + ); + } + + return new this.dtoClass(this, options); + } +} diff --git a/libs/common/src/modules/session/dtos/session.dto.ts b/libs/common/src/modules/session/dtos/session.dto.ts new file mode 100644 index 0000000..7005249 --- /dev/null +++ b/libs/common/src/modules/session/dtos/session.dto.ts @@ -0,0 +1,26 @@ +import { + IsBoolean, + IsDate, + IsNotEmpty, + IsNumber, + IsString, +} from 'class-validator'; + +export class SessionDto { + @IsString() + @IsNotEmpty() + public uuid: string; + + @IsNumber() + @IsNotEmpty() + userId: number; + + @IsDate() + @IsNotEmpty() + public loginTime: Date; + + @IsBoolean() + @IsNotEmpty() + public isLoggedOut: boolean; + +} diff --git a/libs/common/src/modules/session/entities/index.ts b/libs/common/src/modules/session/entities/index.ts new file mode 100644 index 0000000..737110c --- /dev/null +++ b/libs/common/src/modules/session/entities/index.ts @@ -0,0 +1 @@ +export * from './session.entity' \ No newline at end of file diff --git a/libs/common/src/modules/session/entities/session.entity.ts b/libs/common/src/modules/session/entities/session.entity.ts new file mode 100644 index 0000000..e536763 --- /dev/null +++ b/libs/common/src/modules/session/entities/session.entity.ts @@ -0,0 +1,31 @@ +import { Column, Entity } from 'typeorm'; +import { AbstractEntity } from '../../abstract/entities/abstract.entity'; +import { SessionDto } from '../dtos/session.dto'; + +@Entity({ name: 'userSession' }) +export class UserSessionEntity extends AbstractEntity { + @Column({ + nullable: false, + }) + public uuid: string; + + @Column({ + nullable: false, + }) + userId: string; + + @Column({ + nullable: false, + }) + public loginTime: Date; + + @Column({ + nullable: false, + }) + public isLoggedOut: boolean; + + constructor(partial: Partial) { + super(); + Object.assign(this, partial); + } +} diff --git a/libs/common/src/modules/session/repositories/session.repository.ts b/libs/common/src/modules/session/repositories/session.repository.ts new file mode 100644 index 0000000..cf1ce64 --- /dev/null +++ b/libs/common/src/modules/session/repositories/session.repository.ts @@ -0,0 +1,10 @@ +import { DataSource, Repository } from 'typeorm'; +import { Injectable } from '@nestjs/common'; +import { UserSessionEntity } from '../entities'; + +@Injectable() +export class UserSessionRepository extends Repository { + constructor(private dataSource: DataSource) { + super(UserSessionEntity, dataSource.createEntityManager()); + } +} diff --git a/libs/common/src/modules/session/session.repository.module.ts b/libs/common/src/modules/session/session.repository.module.ts new file mode 100644 index 0000000..d963533 --- /dev/null +++ b/libs/common/src/modules/session/session.repository.module.ts @@ -0,0 +1,11 @@ +import { Module } from '@nestjs/common'; +import { TypeOrmModule } from '@nestjs/typeorm'; +import { UserSessionEntity } from './entities'; + +@Module({ + providers: [], + exports: [], + controllers: [], + imports: [TypeOrmModule.forFeature([UserSessionEntity])], +}) +export class UserSessionRepositoryModule {} diff --git a/libs/common/src/modules/user-otp/dtos/index.ts b/libs/common/src/modules/user-otp/dtos/index.ts new file mode 100644 index 0000000..2848db5 --- /dev/null +++ b/libs/common/src/modules/user-otp/dtos/index.ts @@ -0,0 +1 @@ +export * from './user-otp.dto' \ No newline at end of file diff --git a/libs/common/src/modules/user-otp/dtos/user-otp.dto.ts b/libs/common/src/modules/user-otp/dtos/user-otp.dto.ts new file mode 100644 index 0000000..e98c700 --- /dev/null +++ b/libs/common/src/modules/user-otp/dtos/user-otp.dto.ts @@ -0,0 +1,19 @@ +import { IsNotEmpty, IsString } from 'class-validator'; + +export class UserOtpDto { + @IsString() + @IsNotEmpty() + public uuid: string; + + @IsString() + @IsNotEmpty() + public email: string; + + @IsString() + @IsNotEmpty() + public otpCode: string; + + @IsString() + @IsNotEmpty() + public expiryTime: string; +} diff --git a/libs/common/src/modules/user-otp/entities/index.ts b/libs/common/src/modules/user-otp/entities/index.ts new file mode 100644 index 0000000..f1c339e --- /dev/null +++ b/libs/common/src/modules/user-otp/entities/index.ts @@ -0,0 +1 @@ +export * from './user-otp.entity' \ No newline at end of file diff --git a/libs/common/src/modules/user-otp/entities/user-otp.entity.ts b/libs/common/src/modules/user-otp/entities/user-otp.entity.ts new file mode 100644 index 0000000..207ef8c --- /dev/null +++ b/libs/common/src/modules/user-otp/entities/user-otp.entity.ts @@ -0,0 +1,32 @@ +import { Column, Entity } from 'typeorm'; +import { AbstractEntity } from '../../abstract/entities/abstract.entity'; +import { UserOtpDto } from '../dtos'; +import { OtpType } from '@app/common/constants/otp-type.enum'; + +@Entity({ name: 'user-otp' }) +export class UserOtpEntity extends AbstractEntity { + @Column({ + nullable: false, + }) + public uuid: string; + + @Column({ nullable: false }) + email: string; + + @Column({ nullable: false }) + otpCode: string; + + @Column({ nullable: false }) + expiryTime: Date; + + @Column({ + type: 'enum', + enum: Object.values(OtpType), + }) + type: OtpType; + + constructor(partial: Partial) { + super(); + Object.assign(this, partial); + } +} diff --git a/libs/common/src/modules/user-otp/repositories/index.ts b/libs/common/src/modules/user-otp/repositories/index.ts new file mode 100644 index 0000000..e69de29 diff --git a/libs/common/src/modules/user-otp/repositories/user-otp.repository.ts b/libs/common/src/modules/user-otp/repositories/user-otp.repository.ts new file mode 100644 index 0000000..75cff43 --- /dev/null +++ b/libs/common/src/modules/user-otp/repositories/user-otp.repository.ts @@ -0,0 +1,10 @@ +import { DataSource, Repository } from 'typeorm'; +import { Injectable } from '@nestjs/common'; +import { UserOtpEntity } from '../entities'; + +@Injectable() +export class UserOtpRepository extends Repository { + constructor(private dataSource: DataSource) { + super(UserOtpEntity, dataSource.createEntityManager()); + } +} diff --git a/libs/common/src/modules/user-otp/user-otp.repository.module.ts b/libs/common/src/modules/user-otp/user-otp.repository.module.ts new file mode 100644 index 0000000..9286d8b --- /dev/null +++ b/libs/common/src/modules/user-otp/user-otp.repository.module.ts @@ -0,0 +1,11 @@ +import { Module } from '@nestjs/common'; +import { TypeOrmModule } from '@nestjs/typeorm'; +import { UserOtpEntity } from './entities'; + +@Module({ + providers: [], + exports: [], + controllers: [], + imports: [TypeOrmModule.forFeature([UserOtpEntity])], +}) +export class UserOtpRepositoryModule {} diff --git a/libs/common/src/modules/user/dtos/index.ts b/libs/common/src/modules/user/dtos/index.ts new file mode 100644 index 0000000..04e381d --- /dev/null +++ b/libs/common/src/modules/user/dtos/index.ts @@ -0,0 +1 @@ +export * from './user.dto'; diff --git a/libs/common/src/modules/user/dtos/user.dto.ts b/libs/common/src/modules/user/dtos/user.dto.ts new file mode 100644 index 0000000..706ca31 --- /dev/null +++ b/libs/common/src/modules/user/dtos/user.dto.ts @@ -0,0 +1,23 @@ +import { IsNotEmpty, IsString } from 'class-validator'; + +export class UserDto { + @IsString() + @IsNotEmpty() + public uuid: string; + + @IsString() + @IsNotEmpty() + public email: string; + + @IsString() + @IsNotEmpty() + public password: string; + + @IsString() + @IsNotEmpty() + public firstName: string; + + @IsString() + @IsNotEmpty() + public lastName: string; +} diff --git a/libs/common/src/modules/user/entities/index.ts b/libs/common/src/modules/user/entities/index.ts new file mode 100644 index 0000000..e69de29 diff --git a/libs/common/src/modules/user/entities/user.entity.ts b/libs/common/src/modules/user/entities/user.entity.ts new file mode 100644 index 0000000..3fd6748 --- /dev/null +++ b/libs/common/src/modules/user/entities/user.entity.ts @@ -0,0 +1,41 @@ +import { Column, Entity } from 'typeorm'; +import { UserDto } from '../dtos'; +import { AbstractEntity } from '../../abstract/entities/abstract.entity'; + +@Entity({ name: 'user' }) +export class UserEntity extends AbstractEntity { + @Column({ + nullable: false, + }) + public uuid: string; + + @Column({ + nullable: false, + unique: true, + }) + email: string; + + @Column({ + nullable: false, + }) + public password: string; + + @Column() + public firstName: string; + + @Column({ + nullable: false, + }) + public lastName: string; + + @Column({ + nullable: true, + default: false, + }) + public isUserVerified: boolean; + + constructor(partial: Partial) { + super(); + Object.assign(this, partial); + } +} diff --git a/libs/common/src/modules/user/repositories/index.ts b/libs/common/src/modules/user/repositories/index.ts new file mode 100644 index 0000000..9fb5d34 --- /dev/null +++ b/libs/common/src/modules/user/repositories/index.ts @@ -0,0 +1 @@ +export * from './user.repository'; diff --git a/libs/common/src/modules/user/repositories/user.repository.ts b/libs/common/src/modules/user/repositories/user.repository.ts new file mode 100644 index 0000000..f83f14f --- /dev/null +++ b/libs/common/src/modules/user/repositories/user.repository.ts @@ -0,0 +1,10 @@ +import { DataSource, Repository } from 'typeorm'; +import { Injectable } from '@nestjs/common'; +import { UserEntity } from '../entities/user.entity'; + +@Injectable() +export class UserRepository extends Repository { + constructor(private dataSource: DataSource) { + super(UserEntity, dataSource.createEntityManager()); + } +} diff --git a/libs/common/src/modules/user/user.repository.module.ts b/libs/common/src/modules/user/user.repository.module.ts new file mode 100644 index 0000000..1b40c6a --- /dev/null +++ b/libs/common/src/modules/user/user.repository.module.ts @@ -0,0 +1,11 @@ +import { Module } from '@nestjs/common'; +import { TypeOrmModule } from '@nestjs/typeorm'; +import { UserEntity } from './entities/user.entity'; + +@Module({ + providers: [], + exports: [], + controllers: [], + imports: [TypeOrmModule.forFeature([UserEntity])], +}) +export class UserRepositoryModule {} diff --git a/libs/common/src/response/response.decorator.ts b/libs/common/src/response/response.decorator.ts new file mode 100644 index 0000000..407f4e9 --- /dev/null +++ b/libs/common/src/response/response.decorator.ts @@ -0,0 +1,4 @@ +import { SetMetadata } from '@nestjs/common'; + +export const ResponseMessage = (message: string) => + SetMetadata('response_message', message); \ No newline at end of file diff --git a/libs/common/src/response/response.interceptor.ts b/libs/common/src/response/response.interceptor.ts new file mode 100644 index 0000000..7186e9c --- /dev/null +++ b/libs/common/src/response/response.interceptor.ts @@ -0,0 +1,5 @@ +export interface Response { + statusCode: number; + message: string; + data?: T; + } \ No newline at end of file diff --git a/libs/common/src/util/email.service.ts b/libs/common/src/util/email.service.ts new file mode 100644 index 0000000..f181de8 --- /dev/null +++ b/libs/common/src/util/email.service.ts @@ -0,0 +1,38 @@ +import { Injectable } from '@nestjs/common'; +import { ConfigService } from '@nestjs/config'; +import * as nodemailer from 'nodemailer'; + +@Injectable() +export class EmailService { + private user: string; + private pass: string; + + constructor(private readonly configService: ConfigService) { + this.user = this.configService.get('email-config.EMAIL_ID'); + this.pass = this.configService.get('email-config.PASSWORD'); + } + + async sendOTPEmail( + email: string, + subject: string, + message: string, + ): Promise { + + const transporter = nodemailer.createTransport({ + service: 'gmail', + auth: { + user: this.user, + pass: this.pass, + }, + }); + + const mailOptions = { + from: this.user, + to: email, + subject, + text: message, + }; + + await transporter.sendMail(mailOptions); + } +} diff --git a/libs/common/src/util/types.ts b/libs/common/src/util/types.ts new file mode 100644 index 0000000..c04b857 --- /dev/null +++ b/libs/common/src/util/types.ts @@ -0,0 +1,46 @@ +export type Constructor = new ( + ...arguments_: Arguments + ) => T; + + export type Plain = T; + export type Optional = T | undefined; + export type Nullable = T | null; + + export type PathImpl = Key extends string + ? T[Key] extends Record + ? + | `${Key}.${PathImpl> & + string}` + | `${Key}.${Exclude & string}` + : never + : never; + + export type PathImpl2 = PathImpl | keyof T; + + export type Path = keyof T extends string + ? PathImpl2 extends string | keyof T + ? PathImpl2 + : keyof T + : never; + + export type PathValue< + T, + P extends Path + > = P extends `${infer Key}.${infer Rest}` + ? Key extends keyof T + ? Rest extends Path + ? PathValue + : never + : never + : P extends keyof T + ? T[P] + : never; + + export type KeyOfType = { + [P in keyof Required]: Required[P] extends U + ? P + : Required[P] extends U[] + ? P + : never; + }[keyof Entity]; + \ No newline at end of file diff --git a/libs/common/src/util/user-auth.swagger.utils.ts b/libs/common/src/util/user-auth.swagger.utils.ts new file mode 100644 index 0000000..faf1798 --- /dev/null +++ b/libs/common/src/util/user-auth.swagger.utils.ts @@ -0,0 +1,21 @@ +import type { INestApplication } from '@nestjs/common'; +import { DocumentBuilder, SwaggerModule } from '@nestjs/swagger'; + +export function setupSwaggerAuthentication(app: INestApplication): void { + const options = new DocumentBuilder() + .setTitle('Authentication-Service') + .addBearerAuth({ + type: 'http', + scheme: 'bearer', + bearerFormat: 'JWT', + in: 'header', + }) + .build(); + + const document = SwaggerModule.createDocument(app, options); + SwaggerModule.setup('api/authentication/documentation', app, document, { + swaggerOptions: { + persistAuthorization: true, + }, + }); +} diff --git a/libs/common/tsconfig.lib.json b/libs/common/tsconfig.lib.json new file mode 100644 index 0000000..8fdbf52 --- /dev/null +++ b/libs/common/tsconfig.lib.json @@ -0,0 +1,9 @@ +{ + "extends": "../../tsconfig.json", + "compilerOptions": { + "declaration": true, + "outDir": "../../dist/libs/common" + }, + "include": ["src/**/*"], + "exclude": ["node_modules", "dist", "test", "**/*spec.ts"] +} diff --git a/nest-cli.json b/nest-cli.json index 61952b0..e6cd962 100644 --- a/nest-cli.json +++ b/nest-cli.json @@ -27,6 +27,15 @@ "compilerOptions": { "tsConfigPath": "apps/auth/tsconfig.app.json" } + }, + "common": { + "type": "library", + "root": "libs/common", + "entryFile": "index", + "sourceRoot": "libs/common/src", + "compilerOptions": { + "tsConfigPath": "libs/common/tsconfig.lib.json" + } } } } \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index 591269f..4ee53d6 100644 --- a/package-lock.json +++ b/package-lock.json @@ -12,14 +12,27 @@ "@nestjs/common": "^10.0.0", "@nestjs/config": "^3.2.0", "@nestjs/core": "^10.0.0", + "@nestjs/jwt": "^10.2.0", + "@nestjs/passport": "^10.0.3", "@nestjs/platform-express": "^10.0.0", + "@nestjs/swagger": "^7.3.0", + "@nestjs/typeorm": "^10.0.2", "@tuya/tuya-connector-nodejs": "^2.1.2", "axios": "^1.6.7", + "bcryptjs": "^2.4.3", "class-transformer": "^0.5.1", "class-validator": "^0.14.1", + "crypto-js": "^4.2.0", + "express-rate-limit": "^7.1.5", + "helmet": "^7.1.0", "ioredis": "^5.3.2", + "morgan": "^1.10.0", + "nodemailer": "^6.9.10", + "passport-jwt": "^4.0.1", + "pg": "^8.11.3", "reflect-metadata": "^0.2.0", - "rxjs": "^7.8.1" + "rxjs": "^7.8.1", + "typeorm": "^0.3.20" }, "devDependencies": { "@nestjs/cli": "^10.0.0", @@ -902,7 +915,7 @@ "version": "0.8.1", "resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz", "integrity": "sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==", - "dev": true, + "devOptional": true, "dependencies": { "@jridgewell/trace-mapping": "0.3.9" }, @@ -914,7 +927,7 @@ "version": "0.3.9", "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz", "integrity": "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==", - "dev": true, + "devOptional": true, "dependencies": { "@jridgewell/resolve-uri": "^3.0.3", "@jridgewell/sourcemap-codec": "^1.4.10" @@ -1084,7 +1097,6 @@ "version": "8.0.2", "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", "integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==", - "dev": true, "dependencies": { "string-width": "^5.1.2", "string-width-cjs": "npm:string-width@^4.2.0", @@ -1101,7 +1113,6 @@ "version": "6.0.1", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz", "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==", - "dev": true, "engines": { "node": ">=12" }, @@ -1113,7 +1124,6 @@ "version": "6.2.1", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz", "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==", - "dev": true, "engines": { "node": ">=12" }, @@ -1124,14 +1134,12 @@ "node_modules/@isaacs/cliui/node_modules/emoji-regex": { "version": "9.2.2", "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", - "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", - "dev": true + "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==" }, "node_modules/@isaacs/cliui/node_modules/string-width": { "version": "5.1.2", "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", - "dev": true, "dependencies": { "eastasianwidth": "^0.2.0", "emoji-regex": "^9.2.2", @@ -1148,7 +1156,6 @@ "version": "7.1.0", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", - "dev": true, "dependencies": { "ansi-regex": "^6.0.1" }, @@ -1163,7 +1170,6 @@ "version": "8.1.0", "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz", "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==", - "dev": true, "dependencies": { "ansi-styles": "^6.1.0", "string-width": "^5.0.1", @@ -1622,7 +1628,7 @@ "version": "3.1.1", "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.1.tgz", "integrity": "sha512-dSYZh7HhCDtCKm4QakX0xFpsRDqjjtZf/kjI/v3T3Nwt5r8/qz/M19F9ySyOqU94SXBmeG9ttTul+YnR4LOxFA==", - "dev": true, + "devOptional": true, "engines": { "node": ">=6.0.0" } @@ -1650,7 +1656,7 @@ "version": "1.4.15", "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz", "integrity": "sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==", - "dev": true + "devOptional": true }, "node_modules/@jridgewell/trace-mapping": { "version": "0.3.22", @@ -1682,6 +1688,11 @@ "node": ">=8" } }, + "node_modules/@microsoft/tsdoc": { + "version": "0.14.2", + "resolved": "https://registry.npmjs.org/@microsoft/tsdoc/-/tsdoc-0.14.2.tgz", + "integrity": "sha512-9b8mPpKrfeGRuhFH5iO1iwCLeIIsV6+H1sRfxbkoGXIyQE2BTsPd9zqSqQJ+pv5sJ/hT5M1zvOFL02MnEezFug==" + }, "node_modules/@nestjs/cli": { "version": "10.3.2", "resolved": "https://registry.npmjs.org/@nestjs/cli/-/cli-10.3.2.tgz", @@ -1810,6 +1821,46 @@ } } }, + "node_modules/@nestjs/jwt": { + "version": "10.2.0", + "resolved": "https://registry.npmjs.org/@nestjs/jwt/-/jwt-10.2.0.tgz", + "integrity": "sha512-x8cG90SURkEiLOehNaN2aRlotxT0KZESUliOPKKnjWiyJOcWurkF3w345WOX0P4MgFzUjGoZ1Sy0aZnxeihT0g==", + "dependencies": { + "@types/jsonwebtoken": "9.0.5", + "jsonwebtoken": "9.0.2" + }, + "peerDependencies": { + "@nestjs/common": "^8.0.0 || ^9.0.0 || ^10.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", + "integrity": "sha512-bSJv4pd6EY99NX9CjBIyn4TVDoSit82DUZlL4I3bqNfy5Gt+gXTa86i3I/i0iIV9P4hntcGM5GyO+FhZAhxtyg==", + "peerDependencies": { + "@nestjs/common": "^8.0.0 || ^9.0.0 || ^10.0.0", + "class-transformer": "^0.4.0 || ^0.5.0", + "class-validator": "^0.13.0 || ^0.14.0", + "reflect-metadata": "^0.1.12 || ^0.2.0" + }, + "peerDependenciesMeta": { + "class-transformer": { + "optional": true + }, + "class-validator": { + "optional": true + } + } + }, + "node_modules/@nestjs/passport": { + "version": "10.0.3", + "resolved": "https://registry.npmjs.org/@nestjs/passport/-/passport-10.0.3.tgz", + "integrity": "sha512-znJ9Y4S8ZDVY+j4doWAJ8EuuVO7SkQN3yOBmzxbGaXbvcSwFDAdGJ+OMCg52NdzIO4tQoN4pYKx8W6M0ArfFRQ==", + "peerDependencies": { + "@nestjs/common": "^8.0.0 || ^9.0.0 || ^10.0.0", + "passport": "^0.4.0 || ^0.5.0 || ^0.6.0 || ^0.7.0" + } + }, "node_modules/@nestjs/platform-express": { "version": "10.3.3", "resolved": "https://registry.npmjs.org/@nestjs/platform-express/-/platform-express-10.3.3.tgz", @@ -1852,6 +1903,38 @@ "integrity": "sha512-AilxAyFOAcK5wA1+LeaySVBrHsGQvUFCDWXKpZjzaL0PqW+xfBOttn8GNtWKFWqneyMZj41MWF9Kl6iPWLwgOA==", "dev": true }, + "node_modules/@nestjs/swagger": { + "version": "7.3.0", + "resolved": "https://registry.npmjs.org/@nestjs/swagger/-/swagger-7.3.0.tgz", + "integrity": "sha512-zLkfKZ+ioYsIZ3dfv7Bj8YHnZMNAGWFUmx2ZDuLp/fBE4P8BSjB7hldzDueFXsmwaPL90v7lgyd82P+s7KME1Q==", + "dependencies": { + "@microsoft/tsdoc": "^0.14.2", + "@nestjs/mapped-types": "2.0.5", + "js-yaml": "4.1.0", + "lodash": "4.17.21", + "path-to-regexp": "3.2.0", + "swagger-ui-dist": "5.11.2" + }, + "peerDependencies": { + "@fastify/static": "^6.0.0 || ^7.0.0", + "@nestjs/common": "^9.0.0 || ^10.0.0", + "@nestjs/core": "^9.0.0 || ^10.0.0", + "class-transformer": "*", + "class-validator": "*", + "reflect-metadata": "^0.1.12 || ^0.2.0" + }, + "peerDependenciesMeta": { + "@fastify/static": { + "optional": true + }, + "class-transformer": { + "optional": true + }, + "class-validator": { + "optional": true + } + } + }, "node_modules/@nestjs/testing": { "version": "10.3.3", "resolved": "https://registry.npmjs.org/@nestjs/testing/-/testing-10.3.3.tgz", @@ -1879,6 +1962,21 @@ } } }, + "node_modules/@nestjs/typeorm": { + "version": "10.0.2", + "resolved": "https://registry.npmjs.org/@nestjs/typeorm/-/typeorm-10.0.2.tgz", + "integrity": "sha512-H738bJyydK4SQkRCTeh1aFBxoO1E9xdL/HaLGThwrqN95os5mEyAtK7BLADOS+vldP4jDZ2VQPLj4epWwRqCeQ==", + "dependencies": { + "uuid": "9.0.1" + }, + "peerDependencies": { + "@nestjs/common": "^8.0.0 || ^9.0.0 || ^10.0.0", + "@nestjs/core": "^8.0.0 || ^9.0.0 || ^10.0.0", + "reflect-metadata": "^0.1.13 || ^0.2.0", + "rxjs": "^7.2.0", + "typeorm": "^0.3.0" + } + }, "node_modules/@nodelib/fs.scandir": { "version": "2.1.5", "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", @@ -1935,7 +2033,6 @@ "version": "0.11.0", "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", "integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==", - "dev": true, "optional": true, "engines": { "node": ">=14" @@ -1977,29 +2074,34 @@ "@sinonjs/commons": "^3.0.0" } }, + "node_modules/@sqltools/formatter": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/@sqltools/formatter/-/formatter-1.2.5.tgz", + "integrity": "sha512-Uy0+khmZqUrUGm5dmMqVlnvufZRSK0FbYzVgp0UMstm+F5+W2/jnEEQyc9vo1ZR/E5ZI/B1WjjoTqBqwJL6Krw==" + }, "node_modules/@tsconfig/node10": { "version": "1.0.9", "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.9.tgz", "integrity": "sha512-jNsYVVxU8v5g43Erja32laIDHXeoNvFEpX33OK4d6hljo3jDhCBDhx5dhCCTMWUojscpAagGiRkBKxpdl9fxqA==", - "dev": true + "devOptional": true }, "node_modules/@tsconfig/node12": { "version": "1.0.11", "resolved": "https://registry.npmjs.org/@tsconfig/node12/-/node12-1.0.11.tgz", "integrity": "sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==", - "dev": true + "devOptional": true }, "node_modules/@tsconfig/node14": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.3.tgz", "integrity": "sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==", - "dev": true + "devOptional": true }, "node_modules/@tsconfig/node16": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.4.tgz", "integrity": "sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA==", - "dev": true + "devOptional": true }, "node_modules/@tuya/tuya-connector-nodejs": { "version": "2.1.2", @@ -2189,6 +2291,14 @@ "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", "dev": true }, + "node_modules/@types/jsonwebtoken": { + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/@types/jsonwebtoken/-/jsonwebtoken-9.0.5.tgz", + "integrity": "sha512-VRLSGzik+Unrup6BsouBeHsf4d1hOEgYWTm/7Nmw1sXoN1+tRly/Gy/po3yeahnP4jfnQWWAhQAqcNfH7ngOkA==", + "dependencies": { + "@types/node": "*" + } + }, "node_modules/@types/methods": { "version": "1.1.4", "resolved": "https://registry.npmjs.org/@types/methods/-/methods-1.1.4.tgz", @@ -2205,7 +2315,6 @@ "version": "20.11.17", "resolved": "https://registry.npmjs.org/@types/node/-/node-20.11.17.tgz", "integrity": "sha512-QmgQZGWu1Yw9TDyAP9ZzpFJKynYNeOvwMJmaxABfieQoVoiVOS6MN1WSpqpRcbeA5+RW82kraAVxCCJg+780Qw==", - "dev": true, "dependencies": { "undici-types": "~5.26.4" } @@ -2666,7 +2775,7 @@ "version": "8.11.3", "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.11.3.tgz", "integrity": "sha512-Y9rRfJG5jcKOE0CLisYbojUjIrIEE7AGMzA/Sm4BslANhbS+cDMpgBdcPT91oJ7OuJ9hYJBx59RjbhxVnrF8Xg==", - "dev": true, + "devOptional": true, "bin": { "acorn": "bin/acorn" }, @@ -2696,7 +2805,7 @@ "version": "8.3.2", "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.3.2.tgz", "integrity": "sha512-cjkyv4OtNCIeqhHrfS81QWXoCBPExR/J62oyEqepVw8WaQeSqpW2uhuLPh1m9eWhDuOo/jUXVTlifvesOWp/4A==", - "dev": true, + "devOptional": true, "engines": { "node": ">=0.4.0" } @@ -2774,7 +2883,6 @@ "version": "5.0.1", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "dev": true, "engines": { "node": ">=8" } @@ -2793,6 +2901,11 @@ "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, + "node_modules/any-promise": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/any-promise/-/any-promise-1.3.0.tgz", + "integrity": "sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A==" + }, "node_modules/anymatch": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", @@ -2818,6 +2931,14 @@ "url": "https://github.com/sponsors/jonschlinkert" } }, + "node_modules/app-root-path": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/app-root-path/-/app-root-path-3.1.0.tgz", + "integrity": "sha512-biN3PwB2gUtjaYy/isrU3aNWI5w+fAfvHkSvCKeQGxhmYpwKFUxudR3Yya+KqVRHBmEDYh+/lTozYCFbmzX4nA==", + "engines": { + "node": ">= 6.0.0" + } + }, "node_modules/append-field": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/append-field/-/append-field-1.0.0.tgz", @@ -2827,13 +2948,12 @@ "version": "4.1.3", "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz", "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==", - "dev": true + "devOptional": true }, "node_modules/argparse": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", - "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", - "dev": true + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==" }, "node_modules/array-flatten": { "version": "1.1.1", @@ -2995,14 +3115,12 @@ "node_modules/balanced-match": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", - "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", - "dev": true + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==" }, "node_modules/base64-js": { "version": "1.5.1", "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", - "dev": true, "funding": [ { "type": "github", @@ -3018,6 +3136,27 @@ } ] }, + "node_modules/basic-auth": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/basic-auth/-/basic-auth-2.0.1.tgz", + "integrity": "sha512-NF+epuEdnUYVlGuhaxbbq+dvJttwLnGY+YixlXlME5KpQ5W3CnXA5cVTneY3SPbPDRkcjMbifrwmFYcClgOZeg==", + "dependencies": { + "safe-buffer": "5.1.2" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/basic-auth/node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" + }, + "node_modules/bcryptjs": { + "version": "2.4.3", + "resolved": "https://registry.npmjs.org/bcryptjs/-/bcryptjs-2.4.3.tgz", + "integrity": "sha512-V/Hy/X9Vt7f3BbPJEi8BdVFMByHi+jNXrYkW3huaybV/kQ0KJg0Y6PkEMbn+zeT+i+SiKZ/HMqJGIIt4LZDqNQ==" + }, "node_modules/binary-extensions": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", @@ -3092,7 +3231,6 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", - "dev": true, "dependencies": { "balanced-match": "^1.0.0" } @@ -3186,11 +3324,24 @@ "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==" + }, "node_modules/buffer-from": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==" }, + "node_modules/buffer-writer": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/buffer-writer/-/buffer-writer-2.0.0.tgz", + "integrity": "sha512-a7ZpuTZU1TRtnwyCNW3I5dc0wWNC3VR9S++Ewyk2HHZdrO3CQJqSpd+95Us590V6AL7JqUAH2IwZ/398PmNFgw==", + "engines": { + "node": ">=4" + } + }, "node_modules/busboy": { "version": "1.6.0", "resolved": "https://registry.npmjs.org/busboy/-/busboy-1.6.0.tgz", @@ -3376,6 +3527,77 @@ "node": ">=8" } }, + "node_modules/cli-highlight": { + "version": "2.1.11", + "resolved": "https://registry.npmjs.org/cli-highlight/-/cli-highlight-2.1.11.tgz", + "integrity": "sha512-9KDcoEVwyUXrjcJNvHD0NFc/hiwe/WPVYIleQh2O1N2Zro5gWJZ/K+3DGn8w8P/F6FxOgzyC5bxDyHIgCSPhGg==", + "dependencies": { + "chalk": "^4.0.0", + "highlight.js": "^10.7.1", + "mz": "^2.4.0", + "parse5": "^5.1.1", + "parse5-htmlparser2-tree-adapter": "^6.0.0", + "yargs": "^16.0.0" + }, + "bin": { + "highlight": "bin/highlight" + }, + "engines": { + "node": ">=8.0.0", + "npm": ">=5.0.0" + } + }, + "node_modules/cli-highlight/node_modules/cliui": { + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", + "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.0", + "wrap-ansi": "^7.0.0" + } + }, + "node_modules/cli-highlight/node_modules/wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/cli-highlight/node_modules/yargs": { + "version": "16.2.0", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", + "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==", + "dependencies": { + "cliui": "^7.0.2", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.0", + "y18n": "^5.0.5", + "yargs-parser": "^20.2.2" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/cli-highlight/node_modules/yargs-parser": { + "version": "20.2.9", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.9.tgz", + "integrity": "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==", + "engines": { + "node": ">=10" + } + }, "node_modules/cli-spinners": { "version": "2.9.2", "resolved": "https://registry.npmjs.org/cli-spinners/-/cli-spinners-2.9.2.tgz", @@ -3416,7 +3638,6 @@ "version": "8.0.1", "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", - "dev": true, "dependencies": { "string-width": "^4.2.0", "strip-ansi": "^6.0.1", @@ -3430,7 +3651,6 @@ "version": "7.0.0", "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", - "dev": true, "dependencies": { "ansi-styles": "^4.0.0", "string-width": "^4.1.0", @@ -3674,13 +3894,12 @@ "version": "1.1.1", "resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz", "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==", - "dev": true + "devOptional": true }, "node_modules/cross-spawn": { "version": "7.0.3", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", - "dev": true, "dependencies": { "path-key": "^3.1.0", "shebang-command": "^2.0.0", @@ -3690,6 +3909,16 @@ "node": ">= 8" } }, + "node_modules/crypto-js": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/crypto-js/-/crypto-js-4.2.0.tgz", + "integrity": "sha512-KALDyEYgpY+Rlob/iriUtjV6d5Eq+Y191A5g4UqLAi8CyGP9N1+FdVbkc1SxKc2r4YAYqG8JzO2KGL+AizD70Q==" + }, + "node_modules/dayjs": { + "version": "1.11.10", + "resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.11.10.tgz", + "integrity": "sha512-vjAczensTgRcqDERK0SR2XMwsF/tSvnvlv6VcF2GIhg6Sx4yOIt/irsr1RDJsKiIyBzJDpCoXiWWq28MqH2cnQ==" + }, "node_modules/debug": { "version": "4.3.4", "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", @@ -3820,7 +4049,7 @@ "version": "4.0.2", "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", - "dev": true, + "devOptional": true, "engines": { "node": ">=0.3.1" } @@ -3880,8 +4109,15 @@ "node_modules/eastasianwidth": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", - "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==", - "dev": true + "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==" + }, + "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==", + "dependencies": { + "safe-buffer": "^5.0.1" + } }, "node_modules/ee-first": { "version": "1.1.1", @@ -3909,8 +4145,7 @@ "node_modules/emoji-regex": { "version": "8.0.0", "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", - "dev": true + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==" }, "node_modules/encodeurl": { "version": "1.0.2", @@ -3960,7 +4195,6 @@ "version": "3.1.2", "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.2.tgz", "integrity": "sha512-ErCHMCae19vR8vQGe50xIsVomy19rg6gFu3+r3jkEO46suLMWBksvVyoGgQV+jOfl84ZSOSlmv6Gxa89PmTGmA==", - "dev": true, "engines": { "node": ">=6" } @@ -4347,6 +4581,20 @@ "node": ">= 0.10.0" } }, + "node_modules/express-rate-limit": { + "version": "7.1.5", + "resolved": "https://registry.npmjs.org/express-rate-limit/-/express-rate-limit-7.1.5.tgz", + "integrity": "sha512-/iVogxu7ueadrepw1bS0X0kaRC/U0afwiYRSLg68Ts+p4Dc85Q5QKsOnPS/QUjPMHvOJQtBDrZgvkOzf8ejUYw==", + "engines": { + "node": ">= 16" + }, + "funding": { + "url": "https://github.com/sponsors/express-rate-limit" + }, + "peerDependencies": { + "express": "4 || 5 || ^5.0.0-beta.1" + } + }, "node_modules/express/node_modules/body-parser": { "version": "1.20.1", "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.1.tgz", @@ -4673,7 +4921,6 @@ "version": "3.1.1", "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.1.1.tgz", "integrity": "sha512-TMKDUnIte6bfb5nWv7V/caI169OHgvwjb7V4WkeUvbQQdjr5rWKqHFiKWb/fcOwB+CzBT+qbWjvj+DVwRskpIg==", - "dev": true, "dependencies": { "cross-spawn": "^7.0.0", "signal-exit": "^4.0.1" @@ -4840,7 +5087,6 @@ "version": "2.0.5", "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", - "dev": true, "engines": { "node": "6.* || 8.* || >= 10.*" } @@ -4888,7 +5134,6 @@ "version": "10.3.10", "resolved": "https://registry.npmjs.org/glob/-/glob-10.3.10.tgz", "integrity": "sha512-fa46+tv1Ak0UPK1TOy/pZrIybNNt4HCv7SDzwyfiOZkvZLEbjsZkJBPtDHVshZjbecAoAGSC20MjLDG/qr679g==", - "dev": true, "dependencies": { "foreground-child": "^3.1.0", "jackspeak": "^2.3.5", @@ -5043,6 +5288,14 @@ "node": ">= 0.4" } }, + "node_modules/helmet": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/helmet/-/helmet-7.1.0.tgz", + "integrity": "sha512-g+HZqgfbpXdCkme/Cd/mZkV0aV3BZZZSugecH03kl38m/Kmdx8jKjBikpDj2cr+Iynv4KpYEviojNdTJActJAg==", + "engines": { + "node": ">=16.0.0" + } + }, "node_modules/hexoid": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/hexoid/-/hexoid-1.0.0.tgz", @@ -5052,6 +5305,14 @@ "node": ">=8" } }, + "node_modules/highlight.js": { + "version": "10.7.3", + "resolved": "https://registry.npmjs.org/highlight.js/-/highlight.js-10.7.3.tgz", + "integrity": "sha512-tzcUFauisWKNHaRkN4Wjl/ZA07gENAjFl3J/c480dprkGTg5EQstgaNFqBfUqCq54kZRIEcreTsAgF/m2quD7A==", + "engines": { + "node": "*" + } + }, "node_modules/html-escaper": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz", @@ -5097,7 +5358,6 @@ "version": "1.2.1", "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", - "dev": true, "funding": [ { "type": "github", @@ -5290,7 +5550,6 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", - "dev": true, "engines": { "node": ">=8" } @@ -5375,8 +5634,7 @@ "node_modules/isexe": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", - "dev": true + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==" }, "node_modules/istanbul-lib-coverage": { "version": "3.2.2", @@ -5465,7 +5723,6 @@ "version": "2.3.6", "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-2.3.6.tgz", "integrity": "sha512-N3yCS/NegsOBokc8GAdM8UcmfsKiSS8cipheD/nivzr700H+nsMOxJjQnvwOcRYVuFkdH0wGUvW2WbXGmrZGbQ==", - "dev": true, "dependencies": { "@isaacs/cliui": "^8.0.2" }, @@ -6159,7 +6416,6 @@ "version": "4.1.0", "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", - "dev": true, "dependencies": { "argparse": "^2.0.1" }, @@ -6233,6 +6489,46 @@ "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==", + "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==", + "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==", + "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", @@ -6318,11 +6614,41 @@ "resolved": "https://registry.npmjs.org/lodash.defaults/-/lodash.defaults-4.2.0.tgz", "integrity": "sha512-qjxPLHd3r5DnsdGacqOMU6pb/avJzdh9tFX2ymgoZE27BmjXrNy/y4LoaiTeAb+O3gL8AfpJGtqfX/ae2leYYQ==" }, + "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==" + }, "node_modules/lodash.isarguments": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/lodash.isarguments/-/lodash.isarguments-3.1.0.tgz", "integrity": "sha512-chi4NHZlZqZD18a0imDHnZPrDeBbTtVN7GXMwuGdRH9qotxAjYs3aVLKc7zNOG9eddR5Ksd8rvFEBc9SsggPpg==" }, + "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==" + }, + "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==" + }, + "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==" + }, + "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==" + }, + "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==" + }, "node_modules/lodash.memoize": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-4.1.2.tgz", @@ -6335,6 +6661,11 @@ "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", "dev": true }, + "node_modules/lodash.once": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/lodash.once/-/lodash.once-4.1.1.tgz", + "integrity": "sha512-Sb487aTOCr9drQVL8pIxOzVhafOjZN9UU54hiN8PU3uAiSV7lx1yYNpbNmex2PK6dSJoNTSJUUswT651yww3Mg==" + }, "node_modules/log-symbols": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz", @@ -6391,7 +6722,7 @@ "version": "1.3.6", "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==", - "dev": true + "devOptional": true }, "node_modules/makeerror": { "version": "1.0.12", @@ -6518,7 +6849,6 @@ "version": "9.0.3", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.3.tgz", "integrity": "sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg==", - "dev": true, "dependencies": { "brace-expansion": "^2.0.1" }, @@ -6541,7 +6871,6 @@ "version": "7.0.4", "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.0.4.tgz", "integrity": "sha512-jYofLM5Dam9279rdkWzqHozUo4ybjdZmCsDHePy5V/PbBcVMiSZR97gmAy45aqi8CK1lG2ECd356FU86avfwUQ==", - "dev": true, "engines": { "node": ">=16 || 14 >=14.17" } @@ -6557,6 +6886,45 @@ "mkdirp": "bin/cmd.js" } }, + "node_modules/morgan": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/morgan/-/morgan-1.10.0.tgz", + "integrity": "sha512-AbegBVI4sh6El+1gNwvD5YIck7nSA36weD7xvIxG4in80j/UoK8AEGaWnnz8v1GxonMCltmlNs5ZKbGvl9b1XQ==", + "dependencies": { + "basic-auth": "~2.0.1", + "debug": "2.6.9", + "depd": "~2.0.0", + "on-finished": "~2.3.0", + "on-headers": "~1.0.2" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/morgan/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/morgan/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" + }, + "node_modules/morgan/node_modules/on-finished": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz", + "integrity": "sha512-ikqdkGAAyf/X/gPhXGvfgAytDZtDbr+bkNUJ0N9h5MI/dmdgCs3l6hoHrcUv41sRKew3jIwrp4qQDXiK99Utww==", + "dependencies": { + "ee-first": "1.1.1" + }, + "engines": { + "node": ">= 0.8" + } + }, "node_modules/ms": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", @@ -6585,6 +6953,16 @@ "integrity": "sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA==", "dev": true }, + "node_modules/mz": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/mz/-/mz-2.7.0.tgz", + "integrity": "sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==", + "dependencies": { + "any-promise": "^1.0.0", + "object-assign": "^4.0.1", + "thenify-all": "^1.0.0" + } + }, "node_modules/natural-compare": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", @@ -6651,6 +7029,14 @@ "integrity": "sha512-y10wOWt8yZpqXmOgRo77WaHEmhYQYGNA6y421PKsKYWEK8aW+cqAphborZDhqfyKrbZEN92CN1X2KbafY2s7Yw==", "dev": true }, + "node_modules/nodemailer": { + "version": "6.9.10", + "resolved": "https://registry.npmjs.org/nodemailer/-/nodemailer-6.9.10.tgz", + "integrity": "sha512-qtoKfGFhvIFW5kLfrkw2R6Nm6Ur4LNUMykyqu6n9BRKJuyQrqEGwdXXUAbwWEKt33dlWUGXb7rzmJP/p4+O+CA==", + "engines": { + "node": ">=6.0.0" + } + }, "node_modules/normalize-path": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", @@ -6699,6 +7085,14 @@ "node": ">= 0.8" } }, + "node_modules/on-headers": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/on-headers/-/on-headers-1.0.2.tgz", + "integrity": "sha512-pZAE+FJLoyITytdqK0U5s+FIpjN0JP3OzFi/u8Rx+EV5/W+JTWGXG8xFzevE7AjBfDqHv/8vL8qQsIhHnqRkrA==", + "engines": { + "node": ">= 0.8" + } + }, "node_modules/once": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", @@ -6811,6 +7205,11 @@ "node": ">=6" } }, + "node_modules/packet-reader": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/packet-reader/-/packet-reader-1.0.0.tgz", + "integrity": "sha512-HAKu/fG3HpHFO0AA8WE8q2g+gBJaZ9MG7fcKk+IJPLTGAD6Psw4443l+9DGRbOIh3/aXr7Phy0TjilYivJo5XQ==" + }, "node_modules/parent-module": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", @@ -6841,6 +7240,24 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/parse5": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/parse5/-/parse5-5.1.1.tgz", + "integrity": "sha512-ugq4DFI0Ptb+WWjAdOK16+u/nHfiIrcE+sh8kZMaM0WllQKLI9rOUq6c2b7cwPkXdzfQESqvoqK6ug7U/Yyzug==" + }, + "node_modules/parse5-htmlparser2-tree-adapter": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/parse5-htmlparser2-tree-adapter/-/parse5-htmlparser2-tree-adapter-6.0.1.tgz", + "integrity": "sha512-qPuWvbLgvDGilKc5BoicRovlT4MtYT6JfJyBOMDsKoiT+GiuP5qyrPCnR9HcPECIJJmZh5jRndyNThnhhb/vlA==", + "dependencies": { + "parse5": "^6.0.1" + } + }, + "node_modules/parse5-htmlparser2-tree-adapter/node_modules/parse5": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/parse5/-/parse5-6.0.1.tgz", + "integrity": "sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw==" + }, "node_modules/parseurl": { "version": "1.3.3", "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", @@ -6849,6 +7266,41 @@ "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==", + "peer": true, + "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==", + "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", @@ -6871,7 +7323,6 @@ "version": "3.1.1", "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", - "dev": true, "engines": { "node": ">=8" } @@ -6886,7 +7337,6 @@ "version": "1.10.1", "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.10.1.tgz", "integrity": "sha512-MkhCqzzBEpPvxxQ71Md0b1Kk51W01lrYvlMzSUaIzNsODdd7mqhiimSZlr+VegAz5Z6Vzt9Xg2ttE//XBhH3EQ==", - "dev": true, "dependencies": { "lru-cache": "^9.1.1 || ^10.0.0", "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" @@ -6902,7 +7352,6 @@ "version": "10.2.0", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.2.0.tgz", "integrity": "sha512-2bIM8x+VAf6JT4bKAljS1qUWgMsqZRPGJS6FSahIMPVvctcNhyVp7AJu7quxOW9jwkryBReKZY5tY5JYv2n/7Q==", - "dev": true, "engines": { "node": "14 || >=16.14" } @@ -6921,6 +7370,95 @@ "node": ">=8" } }, + "node_modules/pause": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/pause/-/pause-0.0.1.tgz", + "integrity": "sha512-KG8UEiEVkR3wGEb4m5yZkVCzigAD+cVEJck2CzYZO37ZGJfctvVptVO192MwrtPhzONn6go8ylnOdMhKqi4nfg==", + "peer": true + }, + "node_modules/pg": { + "version": "8.11.3", + "resolved": "https://registry.npmjs.org/pg/-/pg-8.11.3.tgz", + "integrity": "sha512-+9iuvG8QfaaUrrph+kpF24cXkH1YOOUeArRNYIxq1viYHZagBxrTno7cecY1Fa44tJeZvaoG+Djpkc3JwehN5g==", + "dependencies": { + "buffer-writer": "2.0.0", + "packet-reader": "1.0.0", + "pg-connection-string": "^2.6.2", + "pg-pool": "^3.6.1", + "pg-protocol": "^1.6.0", + "pg-types": "^2.1.0", + "pgpass": "1.x" + }, + "engines": { + "node": ">= 8.0.0" + }, + "optionalDependencies": { + "pg-cloudflare": "^1.1.1" + }, + "peerDependencies": { + "pg-native": ">=3.0.1" + }, + "peerDependenciesMeta": { + "pg-native": { + "optional": true + } + } + }, + "node_modules/pg-cloudflare": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/pg-cloudflare/-/pg-cloudflare-1.1.1.tgz", + "integrity": "sha512-xWPagP/4B6BgFO+EKz3JONXv3YDgvkbVrGw2mTo3D6tVDQRh1e7cqVGvyR3BE+eQgAvx1XhW/iEASj4/jCWl3Q==", + "optional": true + }, + "node_modules/pg-connection-string": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/pg-connection-string/-/pg-connection-string-2.6.2.tgz", + "integrity": "sha512-ch6OwaeaPYcova4kKZ15sbJ2hKb/VP48ZD2gE7i1J+L4MspCtBMAx8nMgz7bksc7IojCIIWuEhHibSMFH8m8oA==" + }, + "node_modules/pg-int8": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/pg-int8/-/pg-int8-1.0.1.tgz", + "integrity": "sha512-WCtabS6t3c8SkpDBUlb1kjOs7l66xsGdKpIPZsg4wR+B3+u9UAum2odSsF9tnvxg80h4ZxLWMy4pRjOsFIqQpw==", + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/pg-pool": { + "version": "3.6.1", + "resolved": "https://registry.npmjs.org/pg-pool/-/pg-pool-3.6.1.tgz", + "integrity": "sha512-jizsIzhkIitxCGfPRzJn1ZdcosIt3pz9Sh3V01fm1vZnbnCMgmGl5wvGGdNN2EL9Rmb0EcFoCkixH4Pu+sP9Og==", + "peerDependencies": { + "pg": ">=8.0" + } + }, + "node_modules/pg-protocol": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/pg-protocol/-/pg-protocol-1.6.0.tgz", + "integrity": "sha512-M+PDm637OY5WM307051+bsDia5Xej6d9IR4GwJse1qA1DIhiKlksvrneZOYQq42OM+spubpcNYEo2FcKQrDk+Q==" + }, + "node_modules/pg-types": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/pg-types/-/pg-types-2.2.0.tgz", + "integrity": "sha512-qTAAlrEsl8s4OiEQY69wDvcMIdQN6wdz5ojQiOy6YRMuynxenON0O5oCpJI6lshc6scgAY8qvJ2On/p+CXY0GA==", + "dependencies": { + "pg-int8": "1.0.1", + "postgres-array": "~2.0.0", + "postgres-bytea": "~1.0.0", + "postgres-date": "~1.0.4", + "postgres-interval": "^1.1.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/pgpass": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/pgpass/-/pgpass-1.0.5.tgz", + "integrity": "sha512-FdW9r/jQZhSeohs1Z3sI1yxFQNFvMcnmfuj4WBMUTxOrAyLMaTcE1aAMBiTlbMNaXvBCQuVi0R7hd8udDSP7ug==", + "dependencies": { + "split2": "^4.1.0" + } + }, "node_modules/picocolors": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", @@ -7021,6 +7559,41 @@ "node": ">=4" } }, + "node_modules/postgres-array": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/postgres-array/-/postgres-array-2.0.0.tgz", + "integrity": "sha512-VpZrUqU5A69eQyW2c5CA1jtLecCsN2U/bD6VilrFDWq5+5UIEVO7nazS3TEcHf1zuPYO/sqGvUvW62g86RXZuA==", + "engines": { + "node": ">=4" + } + }, + "node_modules/postgres-bytea": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/postgres-bytea/-/postgres-bytea-1.0.0.tgz", + "integrity": "sha512-xy3pmLuQqRBZBXDULy7KbaitYqLcmxigw14Q5sj8QBVLqEwXfeybIKVWiqAXTlcvdvb0+xkOtDbfQMOf4lST1w==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/postgres-date": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/postgres-date/-/postgres-date-1.0.7.tgz", + "integrity": "sha512-suDmjLVQg78nMK2UZ454hAG+OAW+HQPZ6n++TNDUX+L0+uUlLywnoxJKDou51Zm+zTCjrCl0Nq6J9C5hP9vK/Q==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/postgres-interval": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/postgres-interval/-/postgres-interval-1.2.0.tgz", + "integrity": "sha512-9ZhXKM/rw350N1ovuWHbGxnGh/SNJ4cnxHiM0rxE4VN41wsg8P8zWn9hv/buK00RP4WvlOyr/RBDiptyxVbkZQ==", + "dependencies": { + "xtend": "^4.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/prelude-ls": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", @@ -7306,7 +7879,6 @@ "version": "2.1.1", "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", - "dev": true, "engines": { "node": ">=0.10.0" } @@ -7582,7 +8154,6 @@ "version": "7.6.0", "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.0.tgz", "integrity": "sha512-EnwXhrlwXMk9gKu5/flx5sv/an57AkRplG3hTK68W7FRDN+k+OWBj65M7719OkA82XLBxrcX0KSHj+X5COhOVg==", - "dev": true, "dependencies": { "lru-cache": "^6.0.0" }, @@ -7597,7 +8168,6 @@ "version": "6.0.0", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", - "dev": true, "dependencies": { "yallist": "^4.0.0" }, @@ -7608,8 +8178,7 @@ "node_modules/semver/node_modules/yallist": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", - "dev": true + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" }, "node_modules/send": { "version": "0.18.0", @@ -7696,11 +8265,22 @@ "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==" }, + "node_modules/sha.js": { + "version": "2.4.11", + "resolved": "https://registry.npmjs.org/sha.js/-/sha.js-2.4.11.tgz", + "integrity": "sha512-QMEp5B7cftE7APOjk5Y6xgrbWu+WkLVQwk8JNjZ8nKRciZaByEW6MubieAiToS7+dwvrjGhH8jRXz3MVd0AYqQ==", + "dependencies": { + "inherits": "^2.0.1", + "safe-buffer": "^5.0.1" + }, + "bin": { + "sha.js": "bin.js" + } + }, "node_modules/shebang-command": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", - "dev": true, "dependencies": { "shebang-regex": "^3.0.0" }, @@ -7712,7 +8292,6 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", - "dev": true, "engines": { "node": ">=8" } @@ -7797,7 +8376,6 @@ "version": "4.1.0", "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", - "dev": true, "engines": { "node": ">=14" }, @@ -7848,6 +8426,14 @@ "node": ">=0.10.0" } }, + "node_modules/split2": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/split2/-/split2-4.2.0.tgz", + "integrity": "sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg==", + "engines": { + "node": ">= 10.x" + } + }, "node_modules/sprintf-js": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", @@ -7926,7 +8512,6 @@ "version": "4.2.3", "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", - "dev": true, "dependencies": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", @@ -7941,7 +8526,6 @@ "version": "4.2.3", "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", - "dev": true, "dependencies": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", @@ -7955,7 +8539,6 @@ "version": "6.0.1", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "dev": true, "dependencies": { "ansi-regex": "^5.0.1" }, @@ -7968,7 +8551,6 @@ "version": "6.0.1", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "dev": true, "dependencies": { "ansi-regex": "^5.0.1" }, @@ -8075,6 +8657,11 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/swagger-ui-dist": { + "version": "5.11.2", + "resolved": "https://registry.npmjs.org/swagger-ui-dist/-/swagger-ui-dist-5.11.2.tgz", + "integrity": "sha512-jQG0cRgJNMZ7aCoiFofnoojeSaa/+KgWaDlfgs8QN+BXoGMpxeMVY5OEnjq4OlNvF3yjftO8c9GRAgcHlO+u7A==" + }, "node_modules/symbol-observable": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/symbol-observable/-/symbol-observable-4.0.0.tgz", @@ -8258,6 +8845,25 @@ "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==", "dev": true }, + "node_modules/thenify": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/thenify/-/thenify-3.3.1.tgz", + "integrity": "sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw==", + "dependencies": { + "any-promise": "^1.0.0" + } + }, + "node_modules/thenify-all": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/thenify-all/-/thenify-all-1.6.0.tgz", + "integrity": "sha512-RNxQH/qI8/t3thXJDwcstUO4zeqo64+Uy/+sNVRBx4Xn2OX+OZ9oP+iJnNFqplFra2ZUVeKCSa2oVWi3T4uVmA==", + "dependencies": { + "thenify": ">= 3.1.0 < 4" + }, + "engines": { + "node": ">=0.8" + } + }, "node_modules/through": { "version": "2.3.8", "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", @@ -8404,7 +9010,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, "dependencies": { "@cspotcode/source-map-support": "^0.8.0", "@tsconfig/node10": "^1.0.7", @@ -8535,11 +9141,153 @@ "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz", "integrity": "sha512-/aCDEGatGvZ2BIk+HmLf4ifCJFwvKFNb9/JeZPMulfgFracn9QFcAf5GO8B/mweUjSoblS5In0cWhqpfs/5PQA==" }, + "node_modules/typeorm": { + "version": "0.3.20", + "resolved": "https://registry.npmjs.org/typeorm/-/typeorm-0.3.20.tgz", + "integrity": "sha512-sJ0T08dV5eoZroaq9uPKBoNcGslHBR4E4y+EBHs//SiGbblGe7IeduP/IH4ddCcj0qp3PHwDwGnuvqEAnKlq/Q==", + "dependencies": { + "@sqltools/formatter": "^1.2.5", + "app-root-path": "^3.1.0", + "buffer": "^6.0.3", + "chalk": "^4.1.2", + "cli-highlight": "^2.1.11", + "dayjs": "^1.11.9", + "debug": "^4.3.4", + "dotenv": "^16.0.3", + "glob": "^10.3.10", + "mkdirp": "^2.1.3", + "reflect-metadata": "^0.2.1", + "sha.js": "^2.4.11", + "tslib": "^2.5.0", + "uuid": "^9.0.0", + "yargs": "^17.6.2" + }, + "bin": { + "typeorm": "cli.js", + "typeorm-ts-node-commonjs": "cli-ts-node-commonjs.js", + "typeorm-ts-node-esm": "cli-ts-node-esm.js" + }, + "engines": { + "node": ">=16.13.0" + }, + "funding": { + "url": "https://opencollective.com/typeorm" + }, + "peerDependencies": { + "@google-cloud/spanner": "^5.18.0", + "@sap/hana-client": "^2.12.25", + "better-sqlite3": "^7.1.2 || ^8.0.0 || ^9.0.0", + "hdb-pool": "^0.1.6", + "ioredis": "^5.0.4", + "mongodb": "^5.8.0", + "mssql": "^9.1.1 || ^10.0.1", + "mysql2": "^2.2.5 || ^3.0.1", + "oracledb": "^6.3.0", + "pg": "^8.5.1", + "pg-native": "^3.0.0", + "pg-query-stream": "^4.0.0", + "redis": "^3.1.1 || ^4.0.0", + "sql.js": "^1.4.0", + "sqlite3": "^5.0.3", + "ts-node": "^10.7.0", + "typeorm-aurora-data-api-driver": "^2.0.0" + }, + "peerDependenciesMeta": { + "@google-cloud/spanner": { + "optional": true + }, + "@sap/hana-client": { + "optional": true + }, + "better-sqlite3": { + "optional": true + }, + "hdb-pool": { + "optional": true + }, + "ioredis": { + "optional": true + }, + "mongodb": { + "optional": true + }, + "mssql": { + "optional": true + }, + "mysql2": { + "optional": true + }, + "oracledb": { + "optional": true + }, + "pg": { + "optional": true + }, + "pg-native": { + "optional": true + }, + "pg-query-stream": { + "optional": true + }, + "redis": { + "optional": true + }, + "sql.js": { + "optional": true + }, + "sqlite3": { + "optional": true + }, + "ts-node": { + "optional": true + }, + "typeorm-aurora-data-api-driver": { + "optional": true + } + } + }, + "node_modules/typeorm/node_modules/buffer": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz", + "integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "dependencies": { + "base64-js": "^1.3.1", + "ieee754": "^1.2.1" + } + }, + "node_modules/typeorm/node_modules/mkdirp": { + "version": "2.1.6", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-2.1.6.tgz", + "integrity": "sha512-+hEnITedc8LAtIP9u3HJDFIdcLV2vXP33sqLLIzkv1Db1zO/1OxbvYf0Y1OC/S/Qo5dxHXepofhmxL02PsKe+A==", + "bin": { + "mkdirp": "dist/cjs/src/bin.js" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, "node_modules/typescript": { "version": "5.3.3", "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.3.3.tgz", "integrity": "sha512-pXWcraxM0uxAS+tN0AG/BF2TyqmHO014Z070UsJ+pFvYuRSq8KH8DmWpnbXe0pEPDHXZV3FcAbJkijJ5oNEnWw==", - "dev": true, + "devOptional": true, "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" @@ -8562,8 +9310,7 @@ "node_modules/undici-types": { "version": "5.26.5", "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", - "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==", - "dev": true + "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==" }, "node_modules/universalify": { "version": "2.0.1", @@ -8650,7 +9397,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 }, "node_modules/v8-to-istanbul": { "version": "9.2.0", @@ -8818,7 +9565,6 @@ "version": "2.0.2", "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", - "dev": true, "dependencies": { "isexe": "^2.0.0" }, @@ -8848,7 +9594,6 @@ "version": "7.0.0", "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", - "dev": true, "dependencies": { "ansi-styles": "^4.0.0", "string-width": "^4.1.0", @@ -8898,7 +9643,6 @@ "version": "5.0.8", "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", - "dev": true, "engines": { "node": ">=10" } @@ -8913,7 +9657,6 @@ "version": "17.7.2", "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", - "dev": true, "dependencies": { "cliui": "^8.0.1", "escalade": "^3.1.1", @@ -8931,7 +9674,6 @@ "version": "21.1.1", "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", - "dev": true, "engines": { "node": ">=12" } @@ -8940,7 +9682,7 @@ "version": "3.1.1", "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz", "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==", - "dev": true, + "devOptional": true, "engines": { "node": ">=6" } diff --git a/package.json b/package.json index 1ce8852..1cfc0a4 100644 --- a/package.json +++ b/package.json @@ -25,14 +25,27 @@ "@nestjs/common": "^10.0.0", "@nestjs/config": "^3.2.0", "@nestjs/core": "^10.0.0", + "@nestjs/jwt": "^10.2.0", + "@nestjs/passport": "^10.0.3", "@nestjs/platform-express": "^10.0.0", + "@nestjs/swagger": "^7.3.0", + "@nestjs/typeorm": "^10.0.2", "@tuya/tuya-connector-nodejs": "^2.1.2", "axios": "^1.6.7", + "bcryptjs": "^2.4.3", "class-transformer": "^0.5.1", "class-validator": "^0.14.1", + "crypto-js": "^4.2.0", + "express-rate-limit": "^7.1.5", + "helmet": "^7.1.0", "ioredis": "^5.3.2", + "morgan": "^1.10.0", + "nodemailer": "^6.9.10", + "passport-jwt": "^4.0.1", + "pg": "^8.11.3", "reflect-metadata": "^0.2.0", - "rxjs": "^7.8.1" + "rxjs": "^7.8.1", + "typeorm": "^0.3.20" }, "devDependencies": { "@nestjs/cli": "^10.0.0", @@ -74,7 +87,11 @@ "coverageDirectory": "./coverage", "testEnvironment": "node", "roots": [ - "/apps/" - ] + "/apps/", + "/libs/" + ], + "moduleNameMapper": { + "^@app/common(|/.*)$": "/libs/common/src/$1" + } } } diff --git a/tsconfig.json b/tsconfig.json index 0828aa1..4d730b8 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -17,6 +17,13 @@ "strictBindCallApply": false, "forceConsistentCasingInFileNames": false, "noFallthroughCasesInSwitch": false, - "paths": {} + "paths": { + "@app/common": [ + "libs/common/src" + ], + "@app/common/*": [ + "libs/common/src/*" + ] + } } } \ No newline at end of file