From 418a9e2c7adea60d6f8e6b9cfad5a97324fa6fe9 Mon Sep 17 00:00:00 2001 From: unurled Date: Fri, 7 Feb 2025 20:24:16 +0100 Subject: [PATCH] fix eslint errors --- build.ts | 2 +- eslint.config.mjs | 4 +-- src/app.controller.ts | 2 +- src/app.module.ts | 10 ++++-- src/app.ts | 31 +++++++++++------- src/common/middlewares/logger.middleware.ts | 4 +-- src/common/modules/auth/auth.controller.ts | 15 ++++++--- src/common/modules/auth/auth.module.ts | 9 +++++- src/common/modules/auth/auth.service.ts | 23 +++++++++++++ .../modules/auth/decorators/user.decorator.ts | 12 +++++++ src/common/modules/auth/login.controller.ts | 32 +++---------------- src/common/modules/auth/login.service.ts | 6 +--- src/common/modules/auth/register.service.ts | 14 ++------ .../auth/strategies/auth-jwt.strategy.ts | 2 +- src/common/modules/helper/cipher.service.ts | 8 ++--- src/common/modules/helper/prisma.service.ts | 2 ++ src/modules/users/users.controller.ts | 7 ++-- 17 files changed, 105 insertions(+), 78 deletions(-) create mode 100644 src/common/modules/auth/auth.service.ts create mode 100644 src/common/modules/auth/decorators/user.decorator.ts diff --git a/build.ts b/build.ts index 0426fae..3e15dc2 100644 --- a/build.ts +++ b/build.ts @@ -1,4 +1,4 @@ -// @ts-ignore +// @ts-expect-error bun build process await Bun.build({ entrypoints: ['./src/app.ts'], format: 'esm', diff --git a/eslint.config.mjs b/eslint.config.mjs index d4e5af5..d4f0e66 100644 --- a/eslint.config.mjs +++ b/eslint.config.mjs @@ -6,7 +6,7 @@ import tseslint from 'typescript-eslint'; export default tseslint.config( { - ignores: ['eslint.config.mjs'], + ignores: ['eslint.config.mjs', 'dist/', 'node_modules/'], }, eslint.configs.recommended, ...tseslint.configs.recommendedTypeChecked, @@ -29,7 +29,7 @@ export default tseslint.config( rules: { '@typescript-eslint/no-explicit-any': 'off', '@typescript-eslint/no-floating-promises': 'warn', - '@typescript-eslint/no-unsafe-argument': 'warn' + '@typescript-eslint/no-unsafe-argument': 'warn', }, }, ); diff --git a/src/app.controller.ts b/src/app.controller.ts index 3b81836..d212277 100644 --- a/src/app.controller.ts +++ b/src/app.controller.ts @@ -8,7 +8,7 @@ import { ApiTags } from '@nestjs/swagger'; @UseInterceptors(CacheInterceptor) export class AppController { @Get('version') - async getVersion(): Promise { + getVersion(): VersionEntity { return { version: process.env.npm_package_version, }; diff --git a/src/app.module.ts b/src/app.module.ts index 8ed6fed..92264c2 100644 --- a/src/app.module.ts +++ b/src/app.module.ts @@ -24,10 +24,14 @@ import KeyvRedis from '@keyv/redis'; ScheduleModule.forRoot(), CacheModule.registerAsync({ isGlobal: true, - useFactory: async (): Promise => { - const redisUrl: string | undefined = process.env.REDIS_URL; + // eslint-disable-next-line @typescript-eslint/require-await + useFactory: async () => { return { - stores: [redisUrl ? new KeyvRedis(redisUrl) : new CacheableMemory()], + stores: [ + process.env.REDIS_URL + ? new KeyvRedis(process.env.REDIS_URL) + : new CacheableMemory(), + ], }; }, }), diff --git a/src/app.ts b/src/app.ts index f7c1d28..6cc52fc 100644 --- a/src/app.ts +++ b/src/app.ts @@ -13,6 +13,8 @@ import { NestFactory } from '@nestjs/core'; import { AppModule } from './app.module'; import { ConsoleLogger, Logger } from '@nestjs/common'; import * as process from 'process'; +import { FastifyReply } from 'fastify/types/reply'; +import { FastifyRequest } from 'fastify/types/request'; const logger: Logger = new Logger('App'); @@ -50,17 +52,20 @@ async function loadServer(server: NestFastifyApplication) { }); // Middlewares - server.use(new LoggerMiddleware().use); - await server.register(fastifyMultipart as any); - await server.register( - fastifyHelmet as any, - { - contentSecurityPolicy: false, - crossOriginEmbedderPolicy: false, - crossOriginOpenerPolicy: false, - crossOriginResourcePolicy: false, - } as any, - ); + const loggerMiddleware = new LoggerMiddleware(); + const loggerMiddlewareUse = ( + req: FastifyRequest['raw'], + res: FastifyReply['raw'], + next: () => void, + ) => loggerMiddleware.use(req, res, next); + server.use(loggerMiddlewareUse); + await server.register(fastifyMultipart); + await server.register(fastifyHelmet, { + contentSecurityPolicy: false, + crossOriginEmbedderPolicy: false, + crossOriginOpenerPolicy: false, + crossOriginResourcePolicy: false, + }); // Swagger const config = new DocumentBuilder() @@ -88,4 +93,6 @@ async function loadServer(server: NestFastifyApplication) { server.useGlobalPipes(new CustomValidationPipe()); } -bootstrap(); +bootstrap().catch((err) => { + console.error(err); +}); diff --git a/src/common/middlewares/logger.middleware.ts b/src/common/middlewares/logger.middleware.ts index eff7642..72ef7ba 100644 --- a/src/common/middlewares/logger.middleware.ts +++ b/src/common/middlewares/logger.middleware.ts @@ -7,7 +7,7 @@ export class LoggerMiddleware implements NestMiddleware { use(req: FastifyRequest['raw'], res: FastifyReply['raw'], next: () => void) { const startTime = Date.now(); - (res as any).on('finish', () => { + res.on('finish', () => { const path = req.url; try { const protocol = LoggerMiddleware.getProtocol(req); @@ -15,7 +15,7 @@ export class LoggerMiddleware implements NestMiddleware { if (method === 'OPTIONS') return; const statusCode = res.statusCode; const duration = Date.now() - startTime; - const resSize: any = res.getHeader('Content-Length') || '0'; + const resSize = res.getHeader('Content-Length').toString() || '0'; const intResSize = parseInt(resSize); LoggerMiddleware.logger.log( `${protocol} ${method} ${path} ${statusCode} ${duration}ms ${intResSize}`, diff --git a/src/common/modules/auth/auth.controller.ts b/src/common/modules/auth/auth.controller.ts index 3959fa6..b9092db 100644 --- a/src/common/modules/auth/auth.controller.ts +++ b/src/common/modules/auth/auth.controller.ts @@ -1,11 +1,18 @@ -import { Controller, Delete, NotImplementedException } from '@nestjs/common'; +import { Controller, Delete, UseGuards } from '@nestjs/common'; +import { UserEntity } from './models/entities/user.entity'; +import { User } from './decorators/user.decorator'; +import { JwtAuthGuard } from './guards/jwt-auth.guard'; +import { AuthService } from './auth.service'; +import { ApiBearerAuth } from '@nestjs/swagger'; @Controller('auth') export class AuthController { - constructor() {} + constructor(private readonly authService: AuthService) {} @Delete('logout/all') - logoutAll() { - throw new NotImplementedException(); + @UseGuards(JwtAuthGuard) + @ApiBearerAuth() + async logoutAll(@User() user: UserEntity) { + await this.authService.invalidateTokens(user); } } diff --git a/src/common/modules/auth/auth.module.ts b/src/common/modules/auth/auth.module.ts index f2b72da..234023b 100644 --- a/src/common/modules/auth/auth.module.ts +++ b/src/common/modules/auth/auth.module.ts @@ -9,10 +9,17 @@ import { RegisterService } from './register.service'; import { UsersModule } from '../../../modules/users/users.module'; import { ConfigService } from '@nestjs/config'; import { AuthJwtStrategy } from './strategies/auth-jwt.strategy'; +import { AuthService } from './auth.service'; @Module({ controllers: [AuthController, LoginController, RegisterController], - providers: [JwtStrategy, AuthJwtStrategy, LoginService, RegisterService], + providers: [ + JwtStrategy, + AuthJwtStrategy, + LoginService, + RegisterService, + AuthService, + ], imports: [ JwtModule.registerAsync({ inject: [ConfigService], diff --git a/src/common/modules/auth/auth.service.ts b/src/common/modules/auth/auth.service.ts new file mode 100644 index 0000000..84f8944 --- /dev/null +++ b/src/common/modules/auth/auth.service.ts @@ -0,0 +1,23 @@ +import { CipherService } from '../helper/cipher.service'; +import { PrismaService } from '../helper/prisma.service'; +import { Injectable } from '@nestjs/common'; +import { UserEntity } from './models/entities/user.entity'; + +@Injectable() +export class AuthService { + constructor( + private readonly prismaService: PrismaService, + private readonly cipherService: CipherService, + ) {} + + async invalidateTokens(user: UserEntity): Promise { + await this.prismaService.users.update({ + where: { + id: user.id, + }, + data: { + token_id: this.cipherService.generateRandomBytes(), + }, + }); + } +} diff --git a/src/common/modules/auth/decorators/user.decorator.ts b/src/common/modules/auth/decorators/user.decorator.ts new file mode 100644 index 0000000..efb20b7 --- /dev/null +++ b/src/common/modules/auth/decorators/user.decorator.ts @@ -0,0 +1,12 @@ +/* eslint-disable @typescript-eslint/no-unsafe-member-access */ +import { createParamDecorator, ExecutionContext } from '@nestjs/common'; +import { UserEntity } from '../models/entities/user.entity'; + +export const User = createParamDecorator( + (_: unknown, ctx: ExecutionContext): UserEntity => { + // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment + const request: any = ctx.switchToHttp().getRequest(); + if (request.user.user) return request.user.user as UserEntity; + return request.user as UserEntity; + }, +); diff --git a/src/common/modules/auth/login.controller.ts b/src/common/modules/auth/login.controller.ts index 1d1d8d0..2d00866 100644 --- a/src/common/modules/auth/login.controller.ts +++ b/src/common/modules/auth/login.controller.ts @@ -1,10 +1,7 @@ import { Body, Controller, - HttpCode, - NotImplementedException, Post, - Req, UnauthorizedException, UseGuards, } from '@nestjs/common'; @@ -16,6 +13,7 @@ import { LoginService } from './login.service'; import { ApiBearerAuth, ApiTags } from '@nestjs/swagger'; import { AuthGuard } from '@nestjs/passport'; import { UsersService } from '../../../modules/users/users.service'; +import { User } from './decorators/user.decorator'; @Controller('auth/login') @ApiTags('Auth') @@ -60,35 +58,15 @@ export class LoginController { @Post('callback') @UseGuards(AuthGuard('auth-jwt')) @ApiBearerAuth() - loginCallback(@Req() req: any): Promise { - // TODO: Fix this line - req = req.user; - if (req.scope === JwtScope.MAGIC) { - // Check for 2FA/Passkey - const token: string = this.loginService.generateToken( - req.user.id, - req.user.tokenId, - JwtScope.USAGE, - ); - return new LoginPayload({ - user: req.user, - token, - }); - } + loginCallback(@User() user: UserEntity): LoginPayload { const authToken: string = this.loginService.generateToken( - req.user.id, - req.user.tokenId, + user.id, + user.tokenId, JwtScope.USAGE, ); return new LoginPayload({ - user: req.user, + user, token: authToken, }); } - - @Post('passkey') - loginPasskey() { - // Generate JWT token - throw new NotImplementedException(); - } } diff --git a/src/common/modules/auth/login.service.ts b/src/common/modules/auth/login.service.ts index 19f41fe..355f7b0 100644 --- a/src/common/modules/auth/login.service.ts +++ b/src/common/modules/auth/login.service.ts @@ -1,8 +1,4 @@ -import { - Injectable, - NotFoundException, - UnauthorizedException, -} from '@nestjs/common'; +import { Injectable, UnauthorizedException } from '@nestjs/common'; import { UserEntity } from './models/entities/user.entity'; import { CipherService } from '../helper/cipher.service'; import { PrismaService } from '../helper/prisma.service'; diff --git a/src/common/modules/auth/register.service.ts b/src/common/modules/auth/register.service.ts index 16367f0..6f3759a 100644 --- a/src/common/modules/auth/register.service.ts +++ b/src/common/modules/auth/register.service.ts @@ -1,17 +1,7 @@ -import { - ConflictException, - Injectable, - UnauthorizedException, -} from '@nestjs/common'; +import { ConflictException, Injectable } from '@nestjs/common'; import { PrismaService } from '../helper/prisma.service'; import { CipherService } from '../helper/cipher.service'; -import { - EmailVerifications, - Passkeys, - TwoFactorAuth, - Users, -} from '@prisma/client'; -import { UserEntity } from './models/entities/user.entity'; +import { Users } from '@prisma/client'; @Injectable() export class RegisterService { diff --git a/src/common/modules/auth/strategies/auth-jwt.strategy.ts b/src/common/modules/auth/strategies/auth-jwt.strategy.ts index 4696fea..12b47e2 100644 --- a/src/common/modules/auth/strategies/auth-jwt.strategy.ts +++ b/src/common/modules/auth/strategies/auth-jwt.strategy.ts @@ -26,7 +26,7 @@ export class AuthJwtStrategy extends PassportStrategy(Strategy, 'auth-jwt') { throw new UnauthorizedException('Invalid token'); return { user, - scope: payload.scope, + scope: JwtScope.USAGE, }; } } diff --git a/src/common/modules/helper/cipher.service.ts b/src/common/modules/helper/cipher.service.ts index e5ba623..1af95dc 100644 --- a/src/common/modules/helper/cipher.service.ts +++ b/src/common/modules/helper/cipher.service.ts @@ -88,7 +88,7 @@ export class CipherService { decipher.setAuthTag(tag); try { return Buffer.concat([decipher.update(encrypted), decipher.final()]); - } catch (_) { + } catch { throw new Error('Decryption failed'); } } @@ -142,13 +142,13 @@ export class CipherService { // Asymmetric functions generateKeyPair( modulusLength = 4096, - privateEncryptionKey = null, + privateEncryptionKey: string | null = null, ): crypto.KeyPairSyncResult { if (!privateEncryptionKey) console.warn( 'No private encryption key provided, the private key will not be encrypted', ); - let options = undefined; + let options: { cipher: string; passphrase: string } | undefined = undefined; if (privateEncryptionKey) { options = { cipher: 'aes-256-cbc', @@ -185,7 +185,7 @@ export class CipherService { decryptAsymmetric( encryptedContent: string, privateKey: string | Buffer, - privateEncryptionKey = undefined, + privateEncryptionKey: string | undefined = undefined, ): string { const buffer = Buffer.from(encryptedContent, 'base64'); if (!privateEncryptionKey) diff --git a/src/common/modules/helper/prisma.service.ts b/src/common/modules/helper/prisma.service.ts index 85b27c4..0d1184f 100644 --- a/src/common/modules/helper/prisma.service.ts +++ b/src/common/modules/helper/prisma.service.ts @@ -1,3 +1,5 @@ +/* eslint-disable @typescript-eslint/no-unsafe-assignment */ +/* eslint-disable @typescript-eslint/no-unsafe-member-access */ import { Injectable, Logger, OnModuleInit } from '@nestjs/common'; import { PrismaClient } from '@prisma/client'; diff --git a/src/modules/users/users.controller.ts b/src/modules/users/users.controller.ts index 424a1e5..2a8a91a 100644 --- a/src/modules/users/users.controller.ts +++ b/src/modules/users/users.controller.ts @@ -1,7 +1,8 @@ -import { Controller, Get, Req, UseGuards } from '@nestjs/common'; +import { Controller, Get, UseGuards } from '@nestjs/common'; import { JwtAuthGuard } from '../../common/modules/auth/guards/jwt-auth.guard'; import { UserEntity } from '../../common/modules/auth/models/entities/user.entity'; import { ApiBearerAuth } from '@nestjs/swagger'; +import { User } from 'src/common/modules/auth/decorators/user.decorator'; @Controller('users') export class UsersController { @@ -10,7 +11,7 @@ export class UsersController { @Get('me') @UseGuards(JwtAuthGuard) @ApiBearerAuth() - getMyself(@Req() req: any): UserEntity { - return req.user; + getMyself(@User() user: UserEntity): UserEntity { + return user; } }