fix eslint errors
Some checks failed
SonarQube Scan / SonarQube Trigger (push) Failing after 1m8s

This commit is contained in:
unurled 2025-02-07 20:24:16 +01:00
parent b4eb949a3e
commit 418a9e2c7a
Signed by: unurled
GPG key ID: EFC5F5E709B47DDD
17 changed files with 105 additions and 78 deletions

View file

@ -1,4 +1,4 @@
// @ts-ignore // @ts-expect-error bun build process
await Bun.build({ await Bun.build({
entrypoints: ['./src/app.ts'], entrypoints: ['./src/app.ts'],
format: 'esm', format: 'esm',

View file

@ -6,7 +6,7 @@ import tseslint from 'typescript-eslint';
export default tseslint.config( export default tseslint.config(
{ {
ignores: ['eslint.config.mjs'], ignores: ['eslint.config.mjs', 'dist/', 'node_modules/'],
}, },
eslint.configs.recommended, eslint.configs.recommended,
...tseslint.configs.recommendedTypeChecked, ...tseslint.configs.recommendedTypeChecked,
@ -29,7 +29,7 @@ export default tseslint.config(
rules: { rules: {
'@typescript-eslint/no-explicit-any': 'off', '@typescript-eslint/no-explicit-any': 'off',
'@typescript-eslint/no-floating-promises': 'warn', '@typescript-eslint/no-floating-promises': 'warn',
'@typescript-eslint/no-unsafe-argument': 'warn' '@typescript-eslint/no-unsafe-argument': 'warn',
}, },
}, },
); );

View file

@ -8,7 +8,7 @@ import { ApiTags } from '@nestjs/swagger';
@UseInterceptors(CacheInterceptor) @UseInterceptors(CacheInterceptor)
export class AppController { export class AppController {
@Get('version') @Get('version')
async getVersion(): Promise<VersionEntity> { getVersion(): VersionEntity {
return { return {
version: process.env.npm_package_version, version: process.env.npm_package_version,
}; };

View file

@ -24,10 +24,14 @@ import KeyvRedis from '@keyv/redis';
ScheduleModule.forRoot(), ScheduleModule.forRoot(),
CacheModule.registerAsync({ CacheModule.registerAsync({
isGlobal: true, isGlobal: true,
useFactory: async (): Promise<any> => { // eslint-disable-next-line @typescript-eslint/require-await
const redisUrl: string | undefined = process.env.REDIS_URL; useFactory: async () => {
return { return {
stores: [redisUrl ? new KeyvRedis(redisUrl) : new CacheableMemory()], stores: [
process.env.REDIS_URL
? new KeyvRedis(process.env.REDIS_URL)
: new CacheableMemory(),
],
}; };
}, },
}), }),

View file

@ -13,6 +13,8 @@ import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module'; import { AppModule } from './app.module';
import { ConsoleLogger, Logger } from '@nestjs/common'; import { ConsoleLogger, Logger } from '@nestjs/common';
import * as process from 'process'; import * as process from 'process';
import { FastifyReply } from 'fastify/types/reply';
import { FastifyRequest } from 'fastify/types/request';
const logger: Logger = new Logger('App'); const logger: Logger = new Logger('App');
@ -50,17 +52,20 @@ async function loadServer(server: NestFastifyApplication) {
}); });
// Middlewares // Middlewares
server.use(new LoggerMiddleware().use); const loggerMiddleware = new LoggerMiddleware();
await server.register(fastifyMultipart as any); const loggerMiddlewareUse = (
await server.register( req: FastifyRequest['raw'],
fastifyHelmet as any, res: FastifyReply['raw'],
{ next: () => void,
contentSecurityPolicy: false, ) => loggerMiddleware.use(req, res, next);
crossOriginEmbedderPolicy: false, server.use(loggerMiddlewareUse);
crossOriginOpenerPolicy: false, await server.register(fastifyMultipart);
crossOriginResourcePolicy: false, await server.register(fastifyHelmet, {
} as any, contentSecurityPolicy: false,
); crossOriginEmbedderPolicy: false,
crossOriginOpenerPolicy: false,
crossOriginResourcePolicy: false,
});
// Swagger // Swagger
const config = new DocumentBuilder() const config = new DocumentBuilder()
@ -88,4 +93,6 @@ async function loadServer(server: NestFastifyApplication) {
server.useGlobalPipes(new CustomValidationPipe()); server.useGlobalPipes(new CustomValidationPipe());
} }
bootstrap(); bootstrap().catch((err) => {
console.error(err);
});

View file

@ -7,7 +7,7 @@ export class LoggerMiddleware implements NestMiddleware {
use(req: FastifyRequest['raw'], res: FastifyReply['raw'], next: () => void) { use(req: FastifyRequest['raw'], res: FastifyReply['raw'], next: () => void) {
const startTime = Date.now(); const startTime = Date.now();
(res as any).on('finish', () => { res.on('finish', () => {
const path = req.url; const path = req.url;
try { try {
const protocol = LoggerMiddleware.getProtocol(req); const protocol = LoggerMiddleware.getProtocol(req);
@ -15,7 +15,7 @@ export class LoggerMiddleware implements NestMiddleware {
if (method === 'OPTIONS') return; if (method === 'OPTIONS') return;
const statusCode = res.statusCode; const statusCode = res.statusCode;
const duration = Date.now() - startTime; 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); const intResSize = parseInt(resSize);
LoggerMiddleware.logger.log( LoggerMiddleware.logger.log(
`${protocol} ${method} ${path} ${statusCode} ${duration}ms ${intResSize}`, `${protocol} ${method} ${path} ${statusCode} ${duration}ms ${intResSize}`,

View file

@ -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') @Controller('auth')
export class AuthController { export class AuthController {
constructor() {} constructor(private readonly authService: AuthService) {}
@Delete('logout/all') @Delete('logout/all')
logoutAll() { @UseGuards(JwtAuthGuard)
throw new NotImplementedException(); @ApiBearerAuth()
async logoutAll(@User() user: UserEntity) {
await this.authService.invalidateTokens(user);
} }
} }

View file

@ -9,10 +9,17 @@ import { RegisterService } from './register.service';
import { UsersModule } from '../../../modules/users/users.module'; import { UsersModule } from '../../../modules/users/users.module';
import { ConfigService } from '@nestjs/config'; import { ConfigService } from '@nestjs/config';
import { AuthJwtStrategy } from './strategies/auth-jwt.strategy'; import { AuthJwtStrategy } from './strategies/auth-jwt.strategy';
import { AuthService } from './auth.service';
@Module({ @Module({
controllers: [AuthController, LoginController, RegisterController], controllers: [AuthController, LoginController, RegisterController],
providers: [JwtStrategy, AuthJwtStrategy, LoginService, RegisterService], providers: [
JwtStrategy,
AuthJwtStrategy,
LoginService,
RegisterService,
AuthService,
],
imports: [ imports: [
JwtModule.registerAsync({ JwtModule.registerAsync({
inject: [ConfigService], inject: [ConfigService],

View file

@ -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<void> {
await this.prismaService.users.update({
where: {
id: user.id,
},
data: {
token_id: this.cipherService.generateRandomBytes(),
},
});
}
}

View file

@ -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;
},
);

View file

@ -1,10 +1,7 @@
import { import {
Body, Body,
Controller, Controller,
HttpCode,
NotImplementedException,
Post, Post,
Req,
UnauthorizedException, UnauthorizedException,
UseGuards, UseGuards,
} from '@nestjs/common'; } from '@nestjs/common';
@ -16,6 +13,7 @@ import { LoginService } from './login.service';
import { ApiBearerAuth, ApiTags } from '@nestjs/swagger'; import { ApiBearerAuth, ApiTags } from '@nestjs/swagger';
import { AuthGuard } from '@nestjs/passport'; import { AuthGuard } from '@nestjs/passport';
import { UsersService } from '../../../modules/users/users.service'; import { UsersService } from '../../../modules/users/users.service';
import { User } from './decorators/user.decorator';
@Controller('auth/login') @Controller('auth/login')
@ApiTags('Auth') @ApiTags('Auth')
@ -60,35 +58,15 @@ export class LoginController {
@Post('callback') @Post('callback')
@UseGuards(AuthGuard('auth-jwt')) @UseGuards(AuthGuard('auth-jwt'))
@ApiBearerAuth() @ApiBearerAuth()
loginCallback(@Req() req: any): Promise<LoginPayload> { loginCallback(@User() user: UserEntity): LoginPayload {
// 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,
});
}
const authToken: string = this.loginService.generateToken( const authToken: string = this.loginService.generateToken(
req.user.id, user.id,
req.user.tokenId, user.tokenId,
JwtScope.USAGE, JwtScope.USAGE,
); );
return new LoginPayload({ return new LoginPayload({
user: req.user, user,
token: authToken, token: authToken,
}); });
} }
@Post('passkey')
loginPasskey() {
// Generate JWT token
throw new NotImplementedException();
}
} }

View file

@ -1,8 +1,4 @@
import { import { Injectable, UnauthorizedException } from '@nestjs/common';
Injectable,
NotFoundException,
UnauthorizedException,
} from '@nestjs/common';
import { UserEntity } from './models/entities/user.entity'; import { UserEntity } from './models/entities/user.entity';
import { CipherService } from '../helper/cipher.service'; import { CipherService } from '../helper/cipher.service';
import { PrismaService } from '../helper/prisma.service'; import { PrismaService } from '../helper/prisma.service';

View file

@ -1,17 +1,7 @@
import { import { ConflictException, Injectable } from '@nestjs/common';
ConflictException,
Injectable,
UnauthorizedException,
} from '@nestjs/common';
import { PrismaService } from '../helper/prisma.service'; import { PrismaService } from '../helper/prisma.service';
import { CipherService } from '../helper/cipher.service'; import { CipherService } from '../helper/cipher.service';
import { import { Users } from '@prisma/client';
EmailVerifications,
Passkeys,
TwoFactorAuth,
Users,
} from '@prisma/client';
import { UserEntity } from './models/entities/user.entity';
@Injectable() @Injectable()
export class RegisterService { export class RegisterService {

View file

@ -26,7 +26,7 @@ export class AuthJwtStrategy extends PassportStrategy(Strategy, 'auth-jwt') {
throw new UnauthorizedException('Invalid token'); throw new UnauthorizedException('Invalid token');
return { return {
user, user,
scope: payload.scope, scope: JwtScope.USAGE,
}; };
} }
} }

View file

@ -88,7 +88,7 @@ export class CipherService {
decipher.setAuthTag(tag); decipher.setAuthTag(tag);
try { try {
return Buffer.concat([decipher.update(encrypted), decipher.final()]); return Buffer.concat([decipher.update(encrypted), decipher.final()]);
} catch (_) { } catch {
throw new Error('Decryption failed'); throw new Error('Decryption failed');
} }
} }
@ -142,13 +142,13 @@ export class CipherService {
// Asymmetric functions // Asymmetric functions
generateKeyPair( generateKeyPair(
modulusLength = 4096, modulusLength = 4096,
privateEncryptionKey = null, privateEncryptionKey: string | null = null,
): crypto.KeyPairSyncResult<string, string> { ): crypto.KeyPairSyncResult<string, string> {
if (!privateEncryptionKey) if (!privateEncryptionKey)
console.warn( console.warn(
'No private encryption key provided, the private key will not be encrypted', '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) { if (privateEncryptionKey) {
options = { options = {
cipher: 'aes-256-cbc', cipher: 'aes-256-cbc',
@ -185,7 +185,7 @@ export class CipherService {
decryptAsymmetric( decryptAsymmetric(
encryptedContent: string, encryptedContent: string,
privateKey: string | Buffer, privateKey: string | Buffer,
privateEncryptionKey = undefined, privateEncryptionKey: string | undefined = undefined,
): string { ): string {
const buffer = Buffer.from(encryptedContent, 'base64'); const buffer = Buffer.from(encryptedContent, 'base64');
if (!privateEncryptionKey) if (!privateEncryptionKey)

View file

@ -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 { Injectable, Logger, OnModuleInit } from '@nestjs/common';
import { PrismaClient } from '@prisma/client'; import { PrismaClient } from '@prisma/client';

View file

@ -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 { JwtAuthGuard } from '../../common/modules/auth/guards/jwt-auth.guard';
import { UserEntity } from '../../common/modules/auth/models/entities/user.entity'; import { UserEntity } from '../../common/modules/auth/models/entities/user.entity';
import { ApiBearerAuth } from '@nestjs/swagger'; import { ApiBearerAuth } from '@nestjs/swagger';
import { User } from 'src/common/modules/auth/decorators/user.decorator';
@Controller('users') @Controller('users')
export class UsersController { export class UsersController {
@ -10,7 +11,7 @@ export class UsersController {
@Get('me') @Get('me')
@UseGuards(JwtAuthGuard) @UseGuards(JwtAuthGuard)
@ApiBearerAuth() @ApiBearerAuth()
getMyself(@Req() req: any): UserEntity { getMyself(@User() user: UserEntity): UserEntity {
return req.user; return user;
} }
} }