From e8e9bc17a6c66d292a950c225fa5141ce5bd462a Mon Sep 17 00:00:00 2001 From: morteza-mortezai Date: Thu, 12 Mar 2026 22:44:26 +0330 Subject: [PATCH] bugs --- .../decorators/business-id.decorator.ts | 4 +- src/modules/auth/guards/adminAuth.guard.ts | 6 +- src/modules/auth/guards/auth.guard.ts | 72 -------------- src/modules/auth/guards/optinalAuth.guard.ts | 94 ------------------- src/modules/auth/interfaces/IToken-payload.ts | 10 +- src/modules/auth/services/tokens.service.ts | 6 +- src/modules/catalogue/catalogue.controller.ts | 6 +- src/modules/catalogue/catalogue.service.ts | 4 +- .../controllers/uploader.controller.ts | 28 +----- 9 files changed, 18 insertions(+), 212 deletions(-) delete mode 100644 src/modules/auth/guards/auth.guard.ts delete mode 100644 src/modules/auth/guards/optinalAuth.guard.ts diff --git a/src/common/decorators/business-id.decorator.ts b/src/common/decorators/business-id.decorator.ts index e4262f6..ad4e67b 100644 --- a/src/common/decorators/business-id.decorator.ts +++ b/src/common/decorators/business-id.decorator.ts @@ -3,6 +3,6 @@ import type { Request } from 'express'; export const BusinessId = createParamDecorator((data: unknown, ctx: ExecutionContext): string => { - const request = ctx.switchToHttp().getRequest(); - return request.busId || ''; + const request = ctx.switchToHttp().getRequest(); + return request.businessId || ''; }); diff --git a/src/modules/auth/guards/adminAuth.guard.ts b/src/modules/auth/guards/adminAuth.guard.ts index 41647aa..3be3231 100644 --- a/src/modules/auth/guards/adminAuth.guard.ts +++ b/src/modules/auth/guards/adminAuth.guard.ts @@ -15,7 +15,7 @@ import { IAdminTokenPayload } from '../interfaces/IToken-payload'; export interface AdminAuthRequest extends Request { adminId: string; - restId: string; + businessId: string; } @Injectable() @@ -52,13 +52,13 @@ export class AdminAuthGuard implements CanActivate { secret, }); - if (!payload.adminId || !payload.restId) { + if (!payload.adminId || !payload.businessId) { this.logger.error('Invalid token payload structure', payload); throw new UnauthorizedException('Invalid token payload'); } request['adminId'] = payload.adminId; - request['restId'] = payload.restId; + request['businessId'] = payload.businessId; // check if the user has the required permissions // const requiredPermissions = diff --git a/src/modules/auth/guards/auth.guard.ts b/src/modules/auth/guards/auth.guard.ts deleted file mode 100644 index c600f6d..0000000 --- a/src/modules/auth/guards/auth.guard.ts +++ /dev/null @@ -1,72 +0,0 @@ -import { CanActivate, ExecutionContext, Injectable, UnauthorizedException, Inject, Logger } from '@nestjs/common'; -import { Request } from 'express'; -import { JwtService } from '@nestjs/jwt'; -import { ConfigService } from '@nestjs/config'; -import { ITokenPayload } from '../interfaces/IToken-payload'; - -export interface UserAuthRequest extends Request { - userId: string; - restId: string; -} - -@Injectable() -export class AuthGuard implements CanActivate { - private readonly logger = new Logger(AuthGuard.name); - - constructor( - @Inject(JwtService) - private readonly jwtService: JwtService, - @Inject(ConfigService) - private readonly configService: ConfigService, - ) {} - - async canActivate(context: ExecutionContext) { - const request = context.switchToHttp().getRequest(); - - try { - const secret = this.configService.getOrThrow('JWT_SECRET'); - const token = this.extractTokenFromHeader(request); - const slug = this.extractSlugFromHeader(request); - if (!slug) { - throw new UnauthorizedException('Slug is required'); - } - if (!token) { - throw new UnauthorizedException(); - } - - try { - const payload = await this.jwtService.verifyAsync(token, { - secret, - }); - if (!payload.userId || !payload.restId) { - this.logger.error('Invalid token payload structure', payload); - throw new UnauthorizedException('Invalid token payload'); - } - if (payload.slug !== slug) { - throw new UnauthorizedException('Invalid slug'); - } - request['userId'] = payload.userId; - request['restId'] = payload.restId; - } catch { - throw new UnauthorizedException(); - } - - return true; - } catch (err) { - if (err instanceof UnauthorizedException) { - throw err; - } - this.logger.error('error in AuthGuard', err); - throw new UnauthorizedException('Invalid or expired token'); - } - } - private extractTokenFromHeader(request: Request): string | undefined { - const [type, token] = request.headers.authorization?.split(' ') ?? []; - return type === 'Bearer' ? token : undefined; - } - private extractSlugFromHeader(request: Request): string | undefined { - // Fastify normalizes headers to lowercase, so check both cases - const slug = (request.headers['x-slug'] || request.headers['X-Slug']) as string; - return slug; - } -} diff --git a/src/modules/auth/guards/optinalAuth.guard.ts b/src/modules/auth/guards/optinalAuth.guard.ts deleted file mode 100644 index 2cb712b..0000000 --- a/src/modules/auth/guards/optinalAuth.guard.ts +++ /dev/null @@ -1,94 +0,0 @@ -import { CanActivate, ExecutionContext, Injectable, Inject, Logger } from '@nestjs/common'; -import { Request } from 'express'; -import { JwtService } from '@nestjs/jwt'; -import { ConfigService } from '@nestjs/config'; -import { ITokenPayload } from '../interfaces/IToken-payload'; - -export interface UserOptionalAuthRequest extends Request { - userId?: string; - restId?: string; - slug?: string; -} - -@Injectable() -export class OptionalAuthGuard implements CanActivate { - private readonly logger = new Logger(OptionalAuthGuard.name); - - constructor( - @Inject(JwtService) - private readonly jwtService: JwtService, - @Inject(ConfigService) - private readonly configService: ConfigService, - ) {} - - async canActivate(context: ExecutionContext): Promise { - const request = context.switchToHttp().getRequest(); - const token = this.extractTokenFromHeader(request); - const slug = this.extractSlugFromHeader(request); - - // If no token is provided, allow the request without authentication - if (!slug) { - console.log('No slug provided'); - this.logger.warn('No slug provided'); - return false; - } - if (!token) { - request['slug'] = slug; - return true; - } - - // Try to verify the token if it exists - try { - const secret = this.configService.getOrThrow('JWT_SECRET'); - const payload = await this.jwtService.verifyAsync(token, { - secret, - }); - - // Validate payload structure - if (!payload.userId || !payload.restId) { - this.logger.warn('Invalid token payload structure', payload); - // Allow request but don't set user info - request['slug'] = slug; - return true; - } - - // Validate slug if provided - if (slug && payload.slug !== slug) { - this.logger.warn('Token slug does not match header slug', { - tokenSlug: payload.slug, - headerSlug: slug, - }); - // Allow request but don't set user info - request['slug'] = slug; - return true; - } - - // Token is valid, set user info - request['userId'] = payload.userId; - request['restId'] = payload.restId; - request['slug'] = slug; - return true; - } catch (err) { - // Token is invalid or expired, but allow the request anyway - this.logger.debug('Token verification failed in OptionalAuthGuard', { - error: err instanceof Error ? err.message : 'Unknown error', - }); - // Set restId from slug - - request['slug'] = slug; - - return true; - } - } - - private extractTokenFromHeader(request: Request): string | undefined { - const [type, token] = request.headers.authorization?.split(' ') ?? []; - return type === 'Bearer' ? token : undefined; - } - - private extractSlugFromHeader(request: Request): string | undefined { - // Fastify normalizes headers to lowercase, so check both cases - const slug = (request.headers['x-slug'] || request.headers['X-Slug']) as string; - return slug; - } -} diff --git a/src/modules/auth/interfaces/IToken-payload.ts b/src/modules/auth/interfaces/IToken-payload.ts index 457b2ca..cfab2fa 100755 --- a/src/modules/auth/interfaces/IToken-payload.ts +++ b/src/modules/auth/interfaces/IToken-payload.ts @@ -1,10 +1,4 @@ -export interface ITokenPayload { - userId: string; - restId: string; - slug: string; -} - -export interface IAdminTokenPayload { + export interface IAdminTokenPayload { adminId: string; - restId: string; + businessId: string; } diff --git a/src/modules/auth/services/tokens.service.ts b/src/modules/auth/services/tokens.service.ts index fa25659..3756670 100755 --- a/src/modules/auth/services/tokens.service.ts +++ b/src/modules/auth/services/tokens.service.ts @@ -5,7 +5,7 @@ import { Injectable, Logger, UnauthorizedException } from '@nestjs/common'; import { ConfigService } from '@nestjs/config'; import { AuthMessage } from '../../../common/enums/message.enum'; import { RefreshToken } from '../entities/refresh-token.entity'; -import { IAdminTokenPayload, ITokenPayload } from '../interfaces/IToken-payload'; +import { IAdminTokenPayload } from '../interfaces/IToken-payload'; import { JwtService } from '@nestjs/jwt'; import dayjs from 'dayjs'; @@ -26,7 +26,7 @@ export class TokensService { const refreshExpire = this.configService.getOrThrow('REFRESH_TOKEN_EXPIRE'); const accessExpire = this.configService.getOrThrow('JWT_EXPIRATION_TIME'); - const payload: IAdminTokenPayload = { adminId: adminId, restId: businessId } + const payload: IAdminTokenPayload = { adminId, businessId } const accessToken = await this.generateAccessToken(payload, accessExpire); const refreshToken = this.generateRefreshToken(); @@ -40,7 +40,7 @@ export class TokensService { }; } - private generateAccessToken(payload: ITokenPayload | IAdminTokenPayload, expiresIn: number) { + private generateAccessToken(payload: IAdminTokenPayload, expiresIn: number) { // Ensure expiresIn is passed as a string with time unit for reliability // JWT library accepts: number (seconds) or string with unit (e.g., "3600s", "1h") // Using string format is more explicit and prevents unit confusion diff --git a/src/modules/catalogue/catalogue.controller.ts b/src/modules/catalogue/catalogue.controller.ts index 11eca4a..249b97b 100644 --- a/src/modules/catalogue/catalogue.controller.ts +++ b/src/modules/catalogue/catalogue.controller.ts @@ -6,8 +6,10 @@ import { FindCataloguesDto } from './dto/find-catalogues.dto'; import { AdminAuthGuard } from '../auth/guards/adminAuth.guard'; import { BusinessId } from 'src/common/decorators'; import { AdminId } from 'src/common/decorators'; +import { ApiBearerAuth } from '@nestjs/swagger'; @Controller() +@ApiBearerAuth() export class CatalogueController { constructor(private readonly catalogueService: CatalogueService) { } @@ -33,8 +35,8 @@ export class CatalogueController { @Get('admin/catalogue') @UseGuards(AdminAuthGuard) - findAll(@Query() queryDto: FindCataloguesDto, @AdminId() adminId: string) { - return this.catalogueService.findAllAsAdmin(adminId, queryDto); + findAll(@Query() queryDto: FindCataloguesDto, @BusinessId() businessId: string) { + return this.catalogueService.findAllAsAdmin(businessId, queryDto); } @Get('admin/catalogue/:id') diff --git a/src/modules/catalogue/catalogue.service.ts b/src/modules/catalogue/catalogue.service.ts index f0470bf..17b676c 100644 --- a/src/modules/catalogue/catalogue.service.ts +++ b/src/modules/catalogue/catalogue.service.ts @@ -31,8 +31,8 @@ export class CatalogueService { return this.catalogueRepository.findAllPaginated(slug, dto) } - async findAllAsAdmin(adminId: string, dto: FindCataloguesDto) { - const business = await this.businessService.findByAdminIdOrFail(adminId) + async findAllAsAdmin(businessId: string, dto: FindCataloguesDto) { + const business = await this.businessService.findByIdOrFail(businessId) return this.catalogueRepository.findAllPaginated(business.slug, dto) } diff --git a/src/modules/uploader/controllers/uploader.controller.ts b/src/modules/uploader/controllers/uploader.controller.ts index 60d924f..95296d9 100644 --- a/src/modules/uploader/controllers/uploader.controller.ts +++ b/src/modules/uploader/controllers/uploader.controller.ts @@ -4,37 +4,14 @@ import { ApiBody, ApiConsumes, ApiOperation, ApiBearerAuth, ApiTags } from '@nes import { UploadMultipleFileDto, UploadSingleFileDto } from '../DTO/upload-file.dto'; import { UploaderService } from '../providers/uploader.service'; import { AdminAuthGuard } from '../../auth/guards/adminAuth.guard'; -import { AuthGuard } from '../../auth/guards/auth.guard'; @ApiTags('uploader') @Controller() +@ApiBearerAuth() export class UploaderController { - constructor(private readonly uploaderService: UploaderService) {} - - @UseGuards(AuthGuard) - @ApiBearerAuth() - @ApiOperation({ summary: 'Upload a file (admin)' }) - @ApiConsumes('multipart/form-data') - @UseInterceptors(FileInterceptor('file')) - @ApiBody({ type: UploadSingleFileDto }) - @Post('public/single-file') - uploadFile(@UploadedFile() file: File) { - return this.uploaderService.uploadFile(file); - } - - @UseGuards(AuthGuard) - @ApiBearerAuth() - @ApiOperation({ summary: 'Uploads multiple files' }) - @ApiConsumes('multipart/form-data') - @UseInterceptors(FilesInterceptor('files')) - @ApiBody({ type: UploadMultipleFileDto }) - @Post('public/multi-file') - uploadFiles(@UploadedFiles() files: File[]) { - return this.uploaderService.uploadMultiple(files); - } + constructor(private readonly uploaderService: UploaderService) { } @UseGuards(AdminAuthGuard) - @ApiBearerAuth() @ApiOperation({ summary: 'Upload a file (admin)' }) @ApiConsumes('multipart/form-data') @UseInterceptors(FileInterceptor('file')) @@ -45,7 +22,6 @@ export class UploaderController { } @UseGuards(AdminAuthGuard) - @ApiBearerAuth() @ApiOperation({ summary: 'Uploads multiple files' }) @ApiConsumes('multipart/form-data') @UseInterceptors(FilesInterceptor('files'))