Skip to content

first commit#199

Closed
manzano-bjf wants to merge 1 commit intoStarShopCr:mainfrom
manzano-bjf:feat/any_breaking_guarantess
Closed

first commit#199
manzano-bjf wants to merge 1 commit intoStarShopCr:mainfrom
manzano-bjf:feat/any_breaking_guarantess

Conversation

@manzano-bjf
Copy link

@manzano-bjf manzano-bjf commented Sep 30, 2025

Summary by CodeRabbit

  • Refactor

    • User/Auth APIs now use numeric user IDs; responses include id.
    • Removed country/location and role-specific data from user/auth payloads.
    • Orders payload no longer includes escrow/on-chain fields.
    • Simplified authorization (no wallet-ownership checks); reduced telemetry logging.
    • Removed product caching behavior.
  • Documentation

    • Updated migration command, deployment steps (build then start:prod).
    • Added Type Safety guidance and audit command.
  • Chores

    • docker-compose: removed Redis; set default Postgres credentials.
    • Feature flags removed; dependency/test script updates.

@coderabbitai
Copy link

coderabbitai bot commented Sep 30, 2025

Walkthrough

The PR removes escrow/store features, Redis, and various role-specific/user extra fields. It migrates IDs from UUID/string to numeric across entities, DTOs, controllers, and services, switches ownership checks from wallet-based to id-based, tightens TypeScript types (strict mode), simplifies middleware by removing logging/latency, drops caching in products, and updates docs/tests accordingly.

Changes

Cohort / File(s) Summary
Docs
README.md
Updates migration command, deployment steps, and adds a Type Safety section.
DevOps & Tooling
docker-compose.yml, package.json, tsconfig.json
Removes Redis; hardcodes Postgres env; revises test scripts/deps; enables strict TS (strict, noImplicitAny, strictNullChecks).
Bootstrap & Config
src/app.module.ts, src/config/index.ts, src/main.ts
Removes escrow/stores modules and entities; makes TypeORM synchronize env-based; removes request start-time middleware.
HTTP Telemetry Simplification
src/common/filters/http-exception.filter.ts, src/common/interceptors/response.interceptor.ts
Strips RPC/latency logging; retains standardized response formatting.
Middleware Type-Safety
src/middleware/* (async-handler.ts, auth.middleware.ts, paramValidation.middleware.ts, userValidation.middleware.ts, validateRequest.middleware.ts, validation.middleware.ts)
Adds generics and strong typings; replaces some any with unknown and ValidationChain; adjusts role checks and validation flows.
Shared Middleware & Session
src/modules/shared/middleware/* (auth.middleware.ts, session.middleware.ts)
Normalizes role mapping to Role.USER for several inputs; switches role checks to includes; removes any casts.
Global Types
src/types/*, src/modules/shared/types/auth-request.type.ts, src/modules/wishlist/common/types/auth-request.type.ts
Removes location/country/buyerData/sellerData from user shapes; allows numeric user id; defaults generics to unknown.
General DTO Tightening
src/dtos/ProductVariantDTO.ts, src/modules/notifications/dto/notification.dto.ts
Introduces AttributeValueDTO; replaces any[]/Record<string, any> with typed alternatives; adds definite assignments.
Auth Module (ID-based, trimmed fields)
src/modules/auth/... (controllers, dto, dto/.ts, entities/user-role.entity.ts, services/.ts, strategies/jwt.strategy.ts, middleware/.ts, index.ts, tests/.spec.ts)
Moves to numeric userId across APIs/entities; adds id to user response; removes country/location/role-specific data and wallet-ownership exports; simplifies services and JWT strategy; updates tests.
Users Module (ID-based)
src/modules/users/controllers/user.controller.ts, src/modules/users/entities/user.entity.ts, src/modules/users/services/user.service.ts
Routes/services use numeric id; drops payoutWallet/sellerOnchainRegistered/country/location/buyerData/sellerData and stores relation; removes getUserByWalletAddress.
Buyer Requests (ID-based ownership)
src/modules/buyer-requests/... (dto, entities, services)
Converts userId to number; replaces wallet-ownership validation with direct id-based checks; updates signatures and errors.
Offers Service
src/modules/offers/services/offers.service.ts
Removes wallet-ownership validations and User repo; relies on direct repository checks and sellerId ownership.
Orders Pruning
src/modules/orders/dto/order.dto.ts, src/modules/orders/entities/order-item.entity.ts, src/modules/orders/entities/order.entity.ts
Removes on-chain fields and enums (OnchainStatus, OrderItemStatus, milestone/status).
Products (No cache)
src/modules/products/products.module.ts, src/modules/products/services/product.service.ts
Removes AppCacheModule and CacheService usage/decorators.
Reviews (ID-based)
src/modules/reviews/... (controllers, dto, entities, services)
Switches userId to number; coerces req.user.id to number in controllers; adjusts service method signatures.
Files Tests Update
src/modules/files/tests/*
Updates mock user shapes to match removed fields and role expectations.

Sequence Diagram(s)

sequenceDiagram
  autonumber
  actor Client
  participant API as API (AuthController)
  participant AuthSvc as AuthService
  participant UserSvc as UserService
  participant JWT as JWT

  Client->>API: POST /auth/register { walletAddress, role, name?, email? }
  API->>AuthSvc: registerWithWallet(data)
  AuthSvc->>UserSvc: createUser({ walletAddress, role, name, email })
  UserSvc-->>AuthSvc: User { id (number), ... }
  AuthSvc->>JWT: sign({ id, walletAddress, role[] })
  JWT-->>AuthSvc: token, exp
  AuthSvc-->>API: { user { id, walletAddress, name, email, role[] }, token, expiresIn }
  API-->>Client: 200 { success: true, data: { user, token, expiresIn } }
Loading
sequenceDiagram
  autonumber
  actor Client
  participant API as API (UsersController)
  participant UserSvc as UserService

  Client->>API: PUT /users/update/:id
  API->>API: ParseIntPipe on :id
  API->>UserSvc: updateUser(id:number, { name?, email? })
  UserSvc-->>API: Updated User { id:number, ... }
  API-->>Client: 200 { success: true, data: { id, walletAddress, name, email, role[] } }
Loading
sequenceDiagram
  autonumber
  actor Seller
  participant API as API (OffersService)
  participant Repo as OfferRepo
  participant BR as BuyerRequestRepo

  Seller->>API: update(offerId, dto, sellerId:number)
  API->>Repo: findOne(offerId)
  Repo-->>API: Offer { sellerId }
  API->>API: authorize sellerId === request.sellerId
  API->>Repo: save(updatedOffer)
  Repo-->>API: Offer
  API-->>Seller: Offer
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

Suggested reviewers

  • Villarley

Poem

I nibbled through UUID vines,
Found numbers ripe on cleaner lines.
No caches, no chains, lighter load—
Hops along a stricter code.
Logs grow quiet, types stand tall—
Thump-thump! This warren serves us all. 🐇✨

Pre-merge checks and finishing touches

❌ Failed checks (1 warning, 1 inconclusive)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
Title Check ❓ Inconclusive The title “first commit” is overly generic and does not describe any of the significant changes made in the pull request, such as removal of telemetry logging, introduction of strict TypeScript options, and cleanup of feature modules; it fails to convey the primary focus or scope of the updates. Please update the title to a concise, descriptive phrase that highlights the main purpose of the PR— for example, “Remove telemetry and enforce strict TypeScript settings” or “Refactor backend: drop telemetry, tighten types, and clean up modules”—so that reviewers immediately understand its intent.
✅ Passed checks (1 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
✨ Finishing touches
  • 📝 Generate Docstrings
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share
🧪 Early access (Sonnet 4.5): enabled

We are currently testing the Sonnet 4.5 model, which is expected to improve code review quality. However, this model may lead to increased noise levels in the review comments. Please disable the early access features if the noise level causes any inconvenience.

Note:

  • Public repositories are always opted into early access features.
  • You can enable or disable early access features from the CodeRabbit UI or by updating the CodeRabbit configuration file.

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 25

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (41)
docker-compose.yml (1)

1-17: Remove remaining Redis cache references
The codebase still includes Redis-specific logic—imports in src/cache/*, the cache.module, cache.service, interceptors, and the REDIS_URL entry in src/config/env.ts. Remove or refactor all Redis/cache code and configuration to complete the Redis removal.

src/modules/notifications/dto/notification.dto.ts (1)

21-25: Convert userId in UserNotificationDto to a numeric type.

Update userId from string to number and replace @IsString() with @IsNumber() (adding @Type(() => Number) if using class-transformer) to align with the numeric IDs used in your entities.

src/modules/products/services/product.service.ts (2)

170-172: Validate sort order before type assertion.

The code casts order.toUpperCase() to 'ASC' | 'DESC' without validation. If a user provides an invalid sort value like name:invalid, this could lead to unexpected behavior or SQL errors.

Apply this diff to add validation:

     if (sort) {
       const [field, order] = sort.split(':');
+      const sortOrder = order?.toUpperCase();
+      if (sortOrder !== 'ASC' && sortOrder !== 'DESC') {
+        throw new Error('Invalid sort order. Use ASC or DESC');
+      }
-      queryBuilder.orderBy(`product.${field}`, order.toUpperCase() as 'ASC' | 'DESC');
+      queryBuilder.orderBy(`product.${field}`, sortOrder as 'ASC' | 'DESC');
     } else {
       queryBuilder.orderBy('product.createdAt', 'DESC');
     }

170-172: Validate field name to prevent SQL injection.

The code dynamically interpolates the field value into the SQL query without validation. A malicious user could provide sort=constructor:ASC or other field names that could cause errors or potentially expose sensitive data through error messages.

Apply this diff to add field validation:

     if (sort) {
       const [field, order] = sort.split(':');
+      const allowedFields = ['name', 'price', 'createdAt', 'category'];
+      if (!allowedFields.includes(field)) {
+        throw new Error(`Invalid sort field. Allowed fields: ${allowedFields.join(', ')}`);
+      }
       const sortOrder = order?.toUpperCase();
       if (sortOrder !== 'ASC' && sortOrder !== 'DESC') {
         throw new Error('Invalid sort order. Use ASC or DESC');
       }
       queryBuilder.orderBy(`product.${field}`, sortOrder as 'ASC' | 'DESC');
     } else {
       queryBuilder.orderBy('product.createdAt', 'DESC');
     }
src/app.module.ts (2)

46-47: Coding guideline violation: Direct process.env access in TypeORM configuration.

Lines 46, 47, 67, and 68 access process.env directly, violating the coding guideline: "Do not access environment variables via process.env directly; import and use the config object from src/config/env".

Based on coding guidelines.

Refactor to use the config object:

+import { config } from './config';
+
 @Module({
   imports: [
     ConfigModule.forRoot({ isGlobal: true }),
     ScheduleModule.forRoot(),
     TypeOrmModule.forRoot({
       type: 'postgres',
-      url: process.env.DATABASE_URL,
-      ssl: process.env.DB_SSL === 'true' ? { rejectUnauthorized: false } : undefined,
+      url: config.database.url || `postgresql://${config.database.username}:${config.database.password}@${config.database.host}:${config.database.port}/${config.database.name}`,
+      ssl: config.database.ssl ? { rejectUnauthorized: false } : undefined,
       entities: [
         // ... entities
       ],
-      synchronize: process.env.NODE_ENV !== 'production',
-      logging: process.env.NODE_ENV === 'development',
+      synchronize: config.database.synchronize,
+      logging: config.database.logging,
     }),

Note: You may need to add a url field to the database config in src/config/index.ts or construct it from existing fields.

Also applies to: 67-68


40-84: Remove or update lingering escrow tests and docs
The file src/modules/escrow/tests/escrow.integration.spec.ts still contains references to removed routes (/escrow/...) and entities (EscrowAccount, Milestone)—delete or refactor these tests, and update any documentation that mentions escrow functionality.

src/config/index.ts (1)

1-29: Refactor config to use ENV from src/config/env.ts
Import { ENV } from './env' in src/config/index.ts and replace all direct process.env accessors with the corresponding ENV properties (e.g., ENV.PORT, ENV.JWT_SECRET, etc.).

src/modules/files/tests/file.controller.spec.ts (2)

14-14: Replace any type with explicit type.

The responseObj variable is typed as any, which violates the coding guideline to avoid the any type. Use an explicit type or unknown instead.

As per coding guidelines.

Apply this diff:

-  let responseObj: any;
+  let responseObj: { status: jest.Mock; json: jest.Mock };

87-97: Remove invalid role property from mockUser

The User entity (src/modules/users/entities/user.entity.ts) doesn’t define a role field—use the existing userRoles relation instead. Omit role: [Role.USER] from this mock for consistency.

src/modules/orders/dto/order.dto.ts (1)

6-6: Migrate Order and OrderItem ID fields to numeric

  • In src/modules/orders/entities/order.entity.ts (line 23) change
    @PrimaryGeneratedColumn('uuid') id: string
    to
    @PrimaryGeneratedColumn() id: number
  • In src/modules/orders/entities/order-item.entity.ts (lines 8, 14) change
    @PrimaryGeneratedColumn('uuid') id: string
    and
    @Column({ type: 'uuid' }) product_id: string
    to use numeric types (e.g. @PrimaryGeneratedColumn() id: number and @Column({ type: 'int' }) product_id: number)
  • Update DTO at src/modules/orders/dto/order.dto.ts (lines 6, 9, 23) so that both id and product_id are typed as number to match the migrated entities
src/modules/wishlist/tests/wishlist.controller.spec.ts (2)

114-131: Add error case tests for removeFromWishlist.

The test suite only covers the success scenario. Per coding guidelines, both success and error cases should be tested.

Consider adding test cases for:

  • Product not found in wishlist
  • User not found
  • Database errors during removal

Example test structure:

it('should throw NotFoundException when product is not in wishlist', async () => {
  const userId = 'user-id';
  const productId = 'product-id';
  const req = mockRequest({
    user: {
      id: userId,
      walletAddress: 'test-wallet',
      role: [Role.USER],
    },
  }) as unknown as AuthRequest;

  jest
    .spyOn(service, 'removeFromWishlist')
    .mockRejectedValueOnce(new NotFoundException('Product not found in wishlist'));

  await expect(controller.removeFromWishlist(productId, req)).rejects.toThrow(NotFoundException);
});

133-150: Add error case tests for getWishlist.

The test suite only covers the success scenario. Per coding guidelines, both success and error cases should be tested.

Consider adding test cases for:

  • Empty wishlist
  • User not found
  • Database errors during retrieval

Example test structure:

it('should return empty array when user has no wishlist items', async () => {
  const userId = 'user-id';
  const req = mockRequest({
    user: {
      id: userId,
      walletAddress: 'test-wallet',
      role: [Role.USER],
    },
  }) as unknown as AuthRequest;

  jest.spyOn(service, 'getWishlist').mockResolvedValueOnce([]);

  const result = await controller.getWishlist(req);
  expect(result.wishlist).toEqual([]);
});

it('should handle service errors gracefully', async () => {
  const userId = 'user-id';
  const req = mockRequest({
    user: {
      id: userId,
      walletAddress: 'test-wallet',
      role: [Role.USER],
    },
  }) as unknown as AuthRequest;

  jest
    .spyOn(service, 'getWishlist')
    .mockRejectedValueOnce(new Error('Database error'));

  await expect(controller.getWishlist(req)).rejects.toThrow('Database error');
});
src/common/filters/http-exception.filter.ts (3)

12-12: Add explicit return type and replace any type.

The catch method violates coding guidelines:

  1. Missing explicit return type (should be : void)
  2. Parameter exception uses any type instead of unknown or a specific type

As per coding guidelines.

Apply this diff:

-  catch(exception: any, host: ArgumentsHost) {
+  catch(exception: unknown, host: ArgumentsHost): void {

Note: You'll need to add type guards when accessing properties on exception throughout the method since it's now unknown.


29-31: Replace any type assertion with a proper interface.

The type assertion (exceptionResponse as any) violates coding guidelines. Define an interface for the exception response shape.

As per coding guidelines.

Apply this diff to define and use a proper interface:

+interface ExceptionResponse {
+  message?: string;
+  [key: string]: any;
+}
+
 @Catch()
 export class HttpExceptionFilter implements ExceptionFilter {

Then update line 30:

-      } else if (typeof exceptionResponse === 'object' && exceptionResponse !== null) {
-        message = (exceptionResponse as any).message || exception.message || message;
-      }
+      } else if (typeof exceptionResponse === 'object' && exceptionResponse !== null) {
+        const typedResponse = exceptionResponse as ExceptionResponse;
+        message = typedResponse.message || (exception as Error).message || message;
+      }

40-43: Avoid direct process.env access; use the central config.

Import ENV from your config and replace the inline check:

+import { ENV } from '../../config';-      ...(process.env.NODE_ENV === 'development' && {
+      ...(ENV.NODE_ENV === 'development' && {
         error: exception.stack,
         timestamp: new Date().toISOString(),
       }),

This ensures you’re not reaching into process.env directly but using the shared environment config.

src/common/interceptors/response.interceptor.ts (1)

28-31: Remove any type annotation.

The explicit any type on formattedResponse violates coding guidelines. Use the StandardResponse interface suggested in the previous comment.

As per coding guidelines.

Apply this diff:

-        const formattedResponse: any = {
+        const formattedResponse: StandardResponse = {
           success: true,
           data,
         };
src/modules/offers/services/offers.service.ts (2)

98-98: Replace console.log with proper logging.

The coding guidelines explicitly prohibit console.log. Use NestJS Logger instead for proper structured logging.

Apply this diff to use proper logging:

+import { Injectable, NotFoundException, ForbiddenException, BadRequestException, Logger } from '@nestjs/common';

 @Injectable()
 export class OffersService {
+  private readonly logger = new Logger(OffersService.name);
+
   constructor(

Then replace the console.log:

-    console.log(`Offer #${offerId} accepted by buyer #${buyerId}. Other pending offers rejected.`);
+    this.logger.log(`Offer #${offerId} accepted by buyer #${buyerId}. Other pending offers rejected.`);

As per coding guidelines.


121-121: Replace console.log with proper logging.

Same issue as in the accept method. Use the NestJS Logger for structured logging instead of console.log.

Apply this diff:

-    console.log(`Offer #${offerId} rejected by buyer #${buyerId}.`);
+    this.logger.log(`Offer #${offerId} rejected by buyer #${buyerId}.`);

As per coding guidelines.

src/middleware/auth.middleware.ts (2)

5-8: Add explicit return type for the middleware function.

The function lacks an explicit return type annotation.

As per coding guidelines, add a return type:

-export const authMiddleware = (req: Request, res: Response, next: NextFunction) => {
+export const authMiddleware = (req: Request, res: Response, next: NextFunction): void => {
   // Implementación del middleware
   next();
 };

10-17: Add explicit return types for the middleware factory and returned function.

Both the factory function and the returned middleware lack explicit return type annotations.

As per coding guidelines, add return types:

-export const requireRole = (roleName: Role) => {
-  return (req: AuthenticatedRequest, res: Response, next: NextFunction) => {
+export const requireRole = (roleName: Role): ((req: AuthenticatedRequest, res: Response, next: NextFunction) => void) => {
+  return (req: AuthenticatedRequest, res: Response, next: NextFunction): void => {
     if (!req.user || !req.user.role.includes(roleName)) {
       return res.status(403).json({ message: 'Forbidden' });
     }
     next();
   };
 };
src/modules/auth/services/auth.service.ts (4)

48-54: Replace direct process.env access and console.log with proper config and logging.

Lines 49 and 52 violate coding guidelines:

  • Direct process.env.NODE_ENV access (should use the config object from src/config/env)
  • console.log usage (should use NestJS Logger)

As per coding guidelines:

+import { Logger } from '@nestjs/common';
+
+// In the class:
+private readonly logger = new Logger(AuthService.name);
+
 // In verifyStellarSignature:
     if (
-      process.env.NODE_ENV === 'development' &&
+      config.nodeEnv === 'development' &&
       signature === 'base64-encoded-signature-string-here'
     ) {
-      console.log('Development mode: Bypassing signature verification for testing');
+      this.logger.warn('Development mode: Bypassing signature verification for testing');
       return true;
     }

Note: Ensure config exports a nodeEnv property.


60-64: Replace console.error with proper logging.

Line 62 uses console.error, which violates coding guidelines.

Use NestJS Logger instead:

     } catch (error) {
-      console.error('Signature verification error:', error);
+      this.logger.error('Signature verification error:', error);
       return false;
     }

70-75: Add explicit return type for the method.

The method lacks an explicit return type annotation.

As per coding guidelines:

   async registerWithWallet(data: {
     walletAddress: string;
     role: 'buyer' | 'seller';
     name?: string;
     email?: string;
-  }): Promise<{ user: User; token: string; expiresIn: number }> {
+  }): Promise<{ user: User; token: string; expiresIn: number }> {

Wait, the return type is already present in the provided code. Let me re-check the annotated code...

Actually, looking at the code again, line 75 shows the return type is present. This is correct.
[analysis]
Return type is present; no issue here.
[actions]
Review the JWT token generation duplication instead
-->


163-174: Accept numeric id parameter and validate input.

The method accepts a string but immediately converts it to number using Number(id), which can return NaN for invalid inputs. This creates a type inconsistency and potential runtime issue.

Change the signature to accept number directly, or validate the string conversion:

Option 1 (preferred): Accept number directly:

-  async getUserById(id: string): Promise<User> {
+  async getUserById(id: number): Promise<User> {
     const user = await this.userRepository.findOne({
-      where: { id: Number(id) },
+      where: { id },
       relations: ['userRoles', 'userRoles.role'],
     });

Option 2: Validate string conversion:

   async getUserById(id: string): Promise<User> {
+    const numericId = Number(id);
+    if (isNaN(numericId)) {
+      throw new BadRequestError('Invalid user ID format');
+    }
     const user = await this.userRepository.findOne({
-      where: { id: Number(id) },
+      where: { id: numericId },
       relations: ['userRoles', 'userRoles.role'],
     });
src/modules/shared/middleware/session.middleware.ts (1)

8-8: Replace direct process.env access with the config object.

Line 8 accesses process.env.JWT_SECRET directly, which violates coding guidelines.

As per coding guidelines, import and use the config object:

+import { config } from '../../../config';
+
-const jwtService = new JwtService({ secret: process.env.JWT_SECRET });
+const jwtService = new JwtService({ secret: config.jwtSecret });
src/modules/reviews/dto/review.dto.ts (1)

8-16: Type inconsistency between DTOs in this file.

ReviewResponseDTO.userId is now number (line 10), but CreateReviewDto.userId is still typed as optional string (line 40). Similarly, ReviewResponseDTO.productId is number (line 11) while CreateReviewDto.productId is string (line 27).

This mismatch will require manual type coercion and creates confusion about the actual types used by the API.

Consider aligning all ID fields to use consistent numeric types:

 export class CreateReviewDto {
-  @IsString()
+  @IsNumber()
   @IsNotEmpty()
-  productId: string;
+  productId: number;

   @IsNumber()
   @Min(1)
   @Max(5)
   rating: number;

   @IsString()
   @IsNotEmpty()
   comment: string;

-  @IsString()
+  @IsNumber()
   @IsOptional()
-  userId?: string;
+  userId?: number;
 }
src/types/auth-request.type.ts (1)

4-12: Type inconsistency between AppUser.id and AuthenticatedRequest.user.id.

AppUser.id is typed as string (line 5), but AuthenticatedRequest.user.id is now string | number (line 16). Since AuthenticatedRequest.user appears to represent the same user entity, this creates a confusing type mismatch.

If the migration goal is to move fully to numeric IDs, consider:

  1. Updating AppUser.id to also be string | number for consistency
  2. Or documenting that AppUser represents the database entity (string ID) while AuthenticatedRequest.user represents the JWT payload (transitional type)
 export interface AppUser {
-  id: string;
+  id: string | number;
   walletAddress: string;
   name?: string;
   email?: string;
   role: Role[];
   createdAt?: Date;
   updatedAt?: Date;
 }
src/modules/buyer-requests/entities/buyer-request.entity.ts (1)

29-30: Add missing migration for buyer_requests.userId type change
No migration file was found for the change to the userId column on the buyer_requests table—please create and apply a migration to update its type.

src/modules/auth/controllers/role.controller.ts (1)

19-25: Add explicit type for req parameter and provide return type documentation.

The req parameter is untyped (implicit any), which violates the coding guideline against using any. It should be typed as AuthenticatedRequest or a proper Express request type with user.

Apply this diff:

+import { AuthenticatedRequest } from '../../shared/middleware/auth.middleware';
+
   @Get('my-roles')
   @UseGuards(JwtAuthGuard)
-  async getMyRoles(@Req() req): Promise<{ roles: string[] }> {
+  async getMyRoles(@Req() req: AuthenticatedRequest): Promise<{ roles: string[] }> {
     const roles = await this.roleService.getUserRoles(req.user.id);
     return { roles: roles.map((role) => role.name) };
   }

As per coding guidelines

src/modules/shared/middleware/auth.middleware.ts (1)

78-88: Fix requireRole to avoid instantiating dependencies with null.

Line 82 creates an AuthMiddleware instance with null for both constructor parameters (new AuthMiddleware(null, null)) just to call mapRoleToEnum. This is fragile and will cause runtime errors if mapRoleToEnum or any other method tries to access those dependencies.

Extract mapRoleToEnum to a standalone utility function:

Create a new utility file (e.g., src/utils/role-mapper.ts):

import { Role } from '../types/role';

export function mapRoleToEnum(roleName: string): Role {
  switch (roleName.toLowerCase()) {
    case 'admin':
      return Role.ADMIN;
    case 'seller':
      return Role.SELLER;
    case 'buyer':
    case 'user':
      return Role.USER;
    default:
      return Role.USER;
  }
}

Then update this file:

+import { mapRoleToEnum } from '../../../utils/role-mapper';
+
 @Injectable()
 export class AuthMiddleware implements NestMiddleware {
   constructor(
     private readonly jwtService: JwtService,
     private readonly roleService: RoleService
   ) {}

-  public mapRoleToEnum(roleName: string): Role {
-    switch (roleName.toLowerCase()) {
-      case 'admin':
-        return Role.ADMIN;
-      case 'seller':
-        return Role.SELLER;
-      case 'buyer':
-      case 'user':
-        return Role.USER;
-      default:
-        return Role.USER;
-    }
-  }

   async use(req: AuthenticatedRequest, res: Response, next: NextFunction): Promise<void> {
     try {
       // ... (rest of the method)
       const userRoles = await this.roleService.getUserRoles(decoded.id);
-      req.user = { ...decoded, role: userRoles.map((role) => this.mapRoleToEnum(role.name)) };
+      req.user = { ...decoded, role: userRoles.map((role) => mapRoleToEnum(role.name)) };
       // ...
     }
   }
 }

 export const requireRole = (
   roleName: 'buyer' | 'seller' | 'admin'
 ): ((req: AuthenticatedRequest, res: Response, next: NextFunction) => void) => {
   return (req: AuthenticatedRequest, res: Response, next: NextFunction) => {
-    const requiredRole = new AuthMiddleware(null, null).mapRoleToEnum(roleName);
+    const requiredRole = mapRoleToEnum(roleName);
     if (!req.user || !req.user.role.includes(requiredRole)) {
       throw new ReferenceError('Insufficient permissions');
     }
     next();
   };
 };
src/modules/reviews/services/review.service.ts (1)

95-114: Potential authorization bypass: type mismatch in ownership check.

Line 104 performs an ownership check: if (review.userId !== userId). If review.userId is stored as a string in the database but the parameter userId is a number, this comparison will use loose equality semantics that might not work as expected, or it will always fail the check.

Ensure both sides of the comparison have the same type. If the Review entity stores userId as a number, this should work correctly. If it's stored as a string, you need to convert the parameter:

   async deleteReview(userId: number, reviewId: string): Promise<boolean> {
     const review = await this.repository.findOne({
       where: { id: reviewId },
     });

     if (!review) {
       throw new NotFoundError(`Review with ID ${reviewId} not found`);
     }

-    if (review.userId !== userId) {
+    if (Number(review.userId) !== userId) {
       throw new BadRequestError('You can only delete your own reviews');
     }

Or better yet, ensure consistent types across the codebase so no conversion is needed.

src/modules/users/entities/user.entity.ts (1)

16-46: Add migration to drop removed user columns
TypeORM doesn’t automatically remove payoutWallet, sellerOnchainRegistered, country, location, buyerData, sellerData or the stores relation—create a new migration in src/migrations to drop these now-deleted columns.

src/modules/auth/tests/auth.service.spec.ts (1)

200-210: registerWithWallet missing signature verification

The test in src/modules/auth/tests/auth.service.spec.ts lines 200–210 expects an UnauthorizedError on invalid signature, but AuthService.registerWithWallet neither accepts nor verifies any signature and thus can’t throw here. Implement signature verification in registerWithWallet or adjust/remove the invalid-signature test.

src/modules/auth/services/role.service.ts (4)

7-7: Remove duplicate type definition.

The RoleName type is already defined and exported from src/modules/auth/types/role.types.ts (visible in relevant code snippets). This local duplicate violates DRY principles and can lead to inconsistencies if the canonical type is updated.

Apply this diff:

-type RoleName = 'buyer' | 'seller' | 'admin';
+import { RoleName } from '../types/role.types';

44-53: Inconsistent parameter type for userId.

The assignRoleToUser method accepts userId: string and parses it to number (line 50), while related methods like removeRoleFromUser (line 55) accept userId: number directly. This inconsistency forces unnecessary type conversions and creates confusion about the expected type.

Apply this diff to align with numeric ID usage throughout the PR:

-async assignRoleToUser(userId: string, roleName: string): Promise<void> {
+async assignRoleToUser(userId: number, roleName: string): Promise<void> {
   const role = await this.findByName(roleName as RoleName);
   if (!role) {
     throw new Error(`Role ${roleName} not found`);
   }
   await this.userRoleRepository.save({
-    userId: parseInt(userId),
+    userId,
     roleId: role.id,
   });
 }

7-7: Remove duplicate type definition.

The RoleName type is already defined in src/modules/auth/types/role.types.ts. This local redefinition creates maintenance overhead and potential drift.

Apply this diff to use the imported type:

+import { RoleName } from '../types/role.types';
-type RoleName = 'buyer' | 'seller' | 'admin';

44-53: Standardize userId parameter type to number.

The method accepts userId: string but immediately converts it to a number with parseInt. This is inconsistent with other methods in the class (removeRoleFromUser, hasRole, hasAnyRole) that accept number directly.

Apply this diff to align with the numeric ID migration:

-async assignRoleToUser(userId: string, roleName: string): Promise<void> {
+async assignRoleToUser(userId: number, roleName: string): Promise<void> {
   const role = await this.findByName(roleName as RoleName);
   if (!role) {
     throw new Error(`Role ${roleName} not found`);
   }
   await this.userRoleRepository.save({
-    userId: parseInt(userId),
+    userId,
     roleId: role.id,
   });
 }
src/modules/buyer-requests/services/buyer-requests.service.ts (1)

252-284: Add explicit return type.

The private method mapToResponseDto lacks an explicit return type annotation, which violates the coding guideline requiring explicit return types for all functions.

As per coding guidelines.

Apply this diff:

-private mapToResponseDto(buyerRequest: BuyerRequest): BuyerRequestResponseDto {
+private mapToResponseDto(buyerRequest: BuyerRequest): BuyerRequestResponseDto {

Note: The return type is already present in the code, but ensure it's consistently applied if any similar methods exist.

src/modules/users/services/user.service.ts (2)

79-90: Replace any[] return type with explicit type.

The return type Promise<any[]> violates the coding guideline "Do not use the any type; use unknown or specific types instead." Define an explicit return type for orders or use the Order entity type.

As per coding guidelines.

Example fix:

-  async getUserOrders(id: string): Promise<any[]> {
+  async getUserOrders(id: string): Promise<Order[]> {

(Import the Order entity at the top of the file if not already present.)


92-103: Replace any[] return type with explicit type.

The return type Promise<any[]> violates the coding guideline "Do not use the any type; use unknown or specific types instead." Define an explicit return type for wishlist items or use the Wishlist entity type.

As per coding guidelines.

Example fix:

-  async getUserWishlist(id: string): Promise<any[]> {
+  async getUserWishlist(id: string): Promise<Wishlist[]> {

(Import the Wishlist entity at the top of the file if not already present.)

src/modules/users/controllers/user.controller.ts (1)

76-79: Do not access environment variables directly; use config object.

Direct access to process.env.NODE_ENV violates the coding guideline: "Do not access environment variables via process.env directly; import and use the config object from src/config/env."

As per coding guidelines.

Import and use the config object:

+import { config } from '../../../config/env';
+
 ...
     res.cookie('token', result.token, {
       httpOnly: true,
-      secure: process.env.NODE_ENV === 'production',
+      secure: config.NODE_ENV === 'production',
       sameSite: 'strict',
       maxAge: result.expiresIn * 1000,
     });
🧹 Nitpick comments (18)
src/middleware/async-handler.ts (1)

3-9: Good type safety improvement with the generic parameter.

The migration from any to a generic type parameter T = unknown aligns well with the coding guidelines and improves type safety at call sites. The explicit RequestHandler return type and typed parameters are also appropriate.

Consider adding JSDoc for developer experience.

While the function is straightforward, a brief JSDoc comment would help developers understand the purpose and usage of this utility, especially regarding error propagation to Express error handlers.

Example JSDoc:

+/**
+ * Wraps an async Express route handler to automatically catch and forward errors.
+ * @template T - The promise return type of the handler function
+ * @param fn - Async function to wrap
+ * @returns Express RequestHandler that catches promise rejections
+ */
 export const asyncHandler = <T = unknown>(
   fn: (req: Request, res: Response, next: NextFunction) => Promise<T>
 ): RequestHandler => {
tsconfig.json (1)

18-22: LGTM! Strict mode enables comprehensive type safety.

Enabling strict: true is an excellent change that enforces type discipline across the codebase and aligns with the PR's migration to stricter types.

Note: strictNullChecks, noImplicitAny, and strictBindCallApply are already enabled by strict: true, making lines 20-22 redundant. You may optionally remove them for cleaner configuration:

   "skipLibCheck": true,
   "strict": true,
-  "strictNullChecks": true,
-  "noImplicitAny": true,
-  "strictBindCallApply": true,
   "forceConsistentCasingInFileNames": false,
README.md (1)

483-502: Update README audit command for precise any detection
In README.md (line 499), replace the existing grep audit with:

rg '\b(:\s*any\b|<any>|as\s+any\b|any\[\])' src --type ts -g '!*.spec.ts' || echo "No 'any' types found"

This uses word boundaries to avoid false positives and excludes test files (current codebase has zero matches).

src/modules/products/services/product.service.ts (1)

47-50: Consider clarifying the dual repository pattern.

The service initializes two separate repositories (this.repository from AppDataSource and this.productRepository from DatabaseAppDataSource). This creates potential confusion about which repository to use in which context. Different methods use different repositories (e.g., create uses this.repository while createProduct uses this.productRepository), which could lead to inconsistencies.

Consider documenting why two data sources are needed or consolidating to a single repository if both point to the same database.

package.json (2)

8-8: Remove duplicate coverage script.

Both test:coverage (line 8) and test:cov (line 23) run identical commands (jest --coverage). This duplication can cause confusion and maintenance issues.

Apply this diff to remove the duplicate:

     "test:coverage": "jest --coverage",
     "dev": "nest start --watch",
     "build": "nest build",
     "start": "nest start",
     "start:dev": "nest start --watch",
     "start:debug": "nest start --debug --watch",
     "start:prod": "node dist/main",
     "test:crypto": "node test-crypto.js",
     "docs:validate": "swagger-cli validate openapi.yaml",
     "docs:build": "redoc-cli build openapi.yaml -o static-docs.html",
     "typeorm": "typeorm",
     "lint": "eslint . --ext .ts",
     "lint:fix": "eslint . --ext .ts --fix",
     "format": "prettier --write \"src/**/*.ts\"",
     "prepare": "husky install",
-    "test:cov": "jest --coverage",
     "test:debug": "node --inspect-brk -r tsconfig-paths/register -r ts-node/register node_modules/.bin/jest --runInBand",

Also applies to: 23-23


57-57: Consider upgrading to Express v5.

The project uses Express v4.21.2 while NestJS v11 officially supports Express v5 as the default HTTP adapter. Express v5 has been stable since September 2024 and includes routing improvements and security updates.

If there are no specific compatibility concerns, consider upgrading:

-    "express": "^4.21.2",
+    "express": "^5.0.1",

Note: Review the Express v4→v5 migration guide for breaking changes in route matching (wildcard syntax, optional parameters). Based on learnings.

src/modules/files/tests/file.controller.spec.ts (1)

16-42: Consider using NestJS testing utilities for proper dependency injection.

The current setup manually instantiates FileService and FileController, then assigns the service via type assertion. This approach is brittle and doesn't follow NestJS testing best practices.

Consider refactoring to use Test.createTestingModule():

+import { Test, TestingModule } from '@nestjs/testing';
+
 describe('FileController', () => {
   let fileController: FileController;
   let fileService: jest.Mocked<FileService>;
   let mockRequest: Partial<AuthenticatedRequest>;
   let mockResponse: Partial<Response>;
   let responseObj: any;

-  beforeEach(() => {
+  beforeEach(async () => {
     // Reset mocks
     jest.clearAllMocks();

-    fileService = new FileService() as jest.Mocked<FileService>;
-
-    fileController = new FileController();
-    (fileController as unknown as { fileService: FileService }).fileService = fileService;
+    const module: TestingModule = await Test.createTestingModule({
+      controllers: [FileController],
+      providers: [
+        {
+          provide: FileService,
+          useValue: {
+            uploadFile: jest.fn(),
+            getUserFiles: jest.fn(),
+            getFileById: jest.fn(),
+            deleteFile: jest.fn(),
+            getFilesByType: jest.fn(),
+          },
+        },
+      ],
+    }).compile();
+
+    fileController = module.get<FileController>(FileController);
+    fileService = module.get<FileService>(FileService) as jest.Mocked<FileService>;

     responseObj = {
       status: jest.fn().mockReturnThis(),
       json: jest.fn().mockReturnThis(),
     };
     mockResponse = responseObj;

     // Setup basic mock request
     mockRequest = {
       user: {
         id: 'user-uuid',
         walletAddress: '0x123',
         role: [Role.USER],
       },
       fileProvider: 'cloudinary',
       fileType: FileType.IMAGE,
       params: {},
     };
   });
src/modules/wishlist/tests/wishlist.controller.spec.ts (1)

77-112: Align test fixtures with entity types

  • Use numeric values for userId and productId in mocks to match id: number on User and Product entities (you may cast productId to string for controller parameters).
  • Keep walletAddress in the mock since AuthRequest.user and User still require it; remove in a follow-up once those types are updated.
src/modules/auth/services/auth.service.ts (1)

90-97: Extract duplicate JWT token generation logic into a helper method.

The JWT token generation logic is duplicated in lines 90-97 and 124-130. This violates the DRY principle and makes maintenance harder.

Extract into a private helper method:

private generateToken(user: User, role: string): { token: string; expiresIn: number } {
  const token = sign(
    { id: user.id, walletAddress: user.walletAddress, role },
    config.jwtSecret,
    { expiresIn: '1h' }
  );
  return { token, expiresIn: 3600 };
}

Then use it in both places:

-    const role = updatedUser.userRoles?.[0]?.role?.name || 'buyer';
-    const token = sign(
-      { id: updatedUser.id, walletAddress: updatedUser.walletAddress, role },
-      config.jwtSecret,
-      {
-        expiresIn: '1h',
-      }
-    );
-
-    return { user: updatedUser, token, expiresIn: 3600 };
+    const role = updatedUser.userRoles?.[0]?.role?.name || 'buyer';
+    const { token, expiresIn } = this.generateToken(updatedUser, role);
+    return { user: updatedUser, token, expiresIn };

Apply the same pattern at lines 124-130.

Also applies to: 124-130

src/modules/auth/dto/auth-response.dto.ts (1)

102-110: Verify consistency of UserResponseDto.data with UserDto.

The UserResponseDto.data is an inline object type that includes id: number and timestamp fields (createdAt, updatedAt), but it doesn't directly reference or extend UserDto. This creates potential for drift between the two definitions if one is updated without the other.

Consider refactoring to compose the type from UserDto:

 data: {
-   id: number;
-   walletAddress: string;
-   name: string;
-   email: string;
-   role: string;
-   createdAt: Date;
-   updatedAt: Date;
+   createdAt: Date;
+   updatedAt: Date;
+ } & UserDto;

Or create a shared interface that both can reference to reduce duplication and maintain consistency.

src/modules/reviews/services/review.service.ts (1)

20-61: Harmonize UserService.getUserById to use numeric IDs
In ReviewService.createReview (src/modules/reviews/services/review.service.ts:36), we’re converting userId: number to string for userService.getUserById(String(userId)) because getUserById currently takes id: string. Since both the User.id and Review.userId fields are number, update the getUserById signature to async getUserById(id: number): Promise<User> in src/modules/users/services/user.service.ts and remove the String() cast here. This will keep types consistent across services.

src/middleware/userValidation.middleware.ts (2)

9-9: Note: Duplicate type alias across middleware files.

ClassType<T> here is identical to Constructor<T> in src/middleware/validation.middleware.ts (line 5). Consider consolidating these type aliases into a shared types file to reduce duplication and ensure consistency.

For example, create src/middleware/types.ts:

export type Constructor<T> = new (...args: unknown[]) => T;

Then import it in both files:

import { Constructor } from './types';

20-20: Note: Inconsistent validation error status codes.

This middleware returns 422 (Unprocessable Entity) for validation errors, while src/middleware/validation.middleware.ts returns 400 (Bad Request) for similar validation failures. Consider standardizing on one status code for consistency, unless the distinction is intentional (e.g., 422 for DTO validation vs. 400 for express-validator chains).

src/modules/buyer-requests/services/buyer-requests.service.ts (2)

140-151: Consider extracting ownership validation to reduce duplication.

The ownership check pattern (fetch entity, verify existence, verify ownership) is repeated across update, remove, and closeRequest methods. This duplication increases maintenance burden and the risk of inconsistent error handling.

Consider extracting to a private helper method:

private async verifyOwnership(id: number, userId: number): Promise<BuyerRequest> {
  const buyerRequest = await this.buyerRequestRepository.findOne({
    where: { id },
  });

  if (!buyerRequest) {
    throw new NotFoundException('Buyer request not found');
  }

  if (buyerRequest.userId !== userId) {
    throw new ForbiddenException('You can only modify your own buyer requests');
  }

  return buyerRequest;
}

Then simplify the methods:

async update(
  id: number,
  updateBuyerRequestDto: UpdateBuyerRequestDto,
  userId: number
): Promise<BuyerRequestResponseDto> {
  const buyerRequest = await this.verifyOwnership(id, userId);
  
  // Check if request is still open
  if (buyerRequest.status !== BuyerRequestStatus.OPEN) {
    throw new ForbiddenException('Cannot update closed or fulfilled requests');
  }
  // ... rest of update logic
}

Also applies to: 190-201, 300-302


140-151: Consider extracting duplicate ownership validation logic.

The ownership check pattern (fetch entity → check existence → verify ownership) is repeated in update, remove, and closeRequest methods. Extracting this into a private helper would reduce duplication and ensure consistent error messages.

Consider adding a private helper method:

private async validateOwnership(id: number, userId: number): Promise<BuyerRequest> {
  const buyerRequest = await this.buyerRequestRepository.findOne({
    where: { id },
  });

  if (!buyerRequest) {
    throw new NotFoundException('Buyer request not found');
  }

  if (buyerRequest.userId !== userId) {
    throw new ForbiddenException('You can only modify your own buyer requests');
  }

  return buyerRequest;
}

Then simplify the methods:

async update(
  id: number,
  updateBuyerRequestDto: UpdateBuyerRequestDto,
  userId: number
): Promise<BuyerRequestResponseDto> {
  const buyerRequest = await this.validateOwnership(id, userId);
  
  // Check if request is still open
  if (buyerRequest.status !== BuyerRequestStatus.OPEN) {
    throw new ForbiddenException('Cannot update closed or fulfilled requests');
  }
  // ... rest of logic
}

async remove(id: number, userId: number): Promise<void> {
  const buyerRequest = await this.validateOwnership(id, userId);
  await this.buyerRequestRepository.remove(buyerRequest);
}

Also applies to: 190-201

src/modules/users/services/user.service.ts (2)

46-55: Type inconsistency: string parameter parsed to number.

The method accepts id: string but immediately parses it to a number with parseInt(id). Since the controller now uses ParseIntPipe to receive numeric IDs, this service method should accept id: number directly to eliminate redundant conversions and improve type safety.

Apply this diff:

-  async getUserById(id: string): Promise<User> {
+  async getUserById(id: number): Promise<User> {
     const user = await this.userRepository.findOne({
-      where: { id: parseInt(id) },
+      where: { id },
       relations: ['userRoles', 'userRoles.role'],
     });

Then update the call site in the controller at line 151 to pass the numeric userId directly instead of converting it back to a string.


79-81: Align parameter types with ID migration.

Both getUserOrders and getUserWishlist accept id: string but parse it to a number with parseInt(id). Consistent with the PR's migration to numeric IDs, these methods should accept id: number directly to avoid redundant parsing.

Apply this diff:

-  async getUserOrders(id: string): Promise<any[]> {
+  async getUserOrders(id: number): Promise<Order[]> {
     const user = await this.userRepository.findOne({
-      where: { id: parseInt(id) },
+      where: { id },
       relations: ['orders'],
     });

...

-  async getUserWishlist(id: string): Promise<any[]> {
+  async getUserWishlist(id: number): Promise<Wishlist[]> {
     const user = await this.userRepository.findOne({
-      where: { id: parseInt(id) },
+      where: { id },
       relations: ['wishlist'],
     });

Also applies to: 92-94

src/modules/users/controllers/user.controller.ts (1)

151-151: Redundant type conversion from number to string.

The userId parameter is parsed to a number via ParseIntPipe, but then converted back to a string with String(userId) when calling userService.getUserById. This indicates a design inconsistency. The service method should accept number directly to align with the ID migration.

After updating userService.getUserById to accept number (as suggested in the service file review), remove the string conversion:

-    const user = await this.userService.getUserById(String(userId));
+    const user = await this.userService.getUserById(userId);
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between b80d6d8 and a31615d.

⛔ Files ignored due to path filters (1)
  • package-lock.json is excluded by !**/package-lock.json
📒 Files selected for processing (58)
  • README.md (2 hunks)
  • docker-compose.yml (1 hunks)
  • package.json (2 hunks)
  • src/app.module.ts (2 hunks)
  • src/common/filters/http-exception.filter.ts (1 hunks)
  • src/common/interceptors/response.interceptor.ts (1 hunks)
  • src/config/index.ts (1 hunks)
  • src/dtos/ProductVariantDTO.ts (1 hunks)
  • src/dtos/UserDTO.ts (0 hunks)
  • src/main.ts (0 hunks)
  • src/middleware/async-handler.ts (1 hunks)
  • src/middleware/auth.middleware.ts (1 hunks)
  • src/middleware/paramValidation.middleware.ts (1 hunks)
  • src/middleware/userValidation.middleware.ts (1 hunks)
  • src/middleware/validateRequest.middleware.ts (1 hunks)
  • src/middleware/validation.middleware.ts (1 hunks)
  • src/modules/auth/auth.module.ts (0 hunks)
  • src/modules/auth/controllers/auth.controller.ts (3 hunks)
  • src/modules/auth/controllers/role.controller.ts (1 hunks)
  • src/modules/auth/dto/auth-response.dto.ts (5 hunks)
  • src/modules/auth/dto/auth.dto.ts (1 hunks)
  • src/modules/auth/entities/user-role.entity.ts (1 hunks)
  • src/modules/auth/index.ts (0 hunks)
  • src/modules/auth/middleware/authorize-roles.middleware.ts (1 hunks)
  • src/modules/auth/middleware/jwt-auth.middleware.ts (1 hunks)
  • src/modules/auth/services/auth.service.ts (9 hunks)
  • src/modules/auth/services/role.service.ts (1 hunks)
  • src/modules/auth/strategies/jwt.strategy.ts (1 hunks)
  • src/modules/auth/tests/auth.service.spec.ts (4 hunks)
  • src/modules/buyer-requests/dto/buyer-request-response.dto.ts (1 hunks)
  • src/modules/buyer-requests/entities/buyer-request.entity.ts (1 hunks)
  • src/modules/buyer-requests/services/buyer-requests.service.ts (4 hunks)
  • src/modules/files/tests/file.controller.spec.ts (1 hunks)
  • src/modules/files/tests/file.service.spec.ts (0 hunks)
  • src/modules/files/tests/test-utils.ts (0 hunks)
  • src/modules/notifications/dto/notification.dto.ts (1 hunks)
  • src/modules/offers/services/offers.service.ts (2 hunks)
  • src/modules/orders/dto/order.dto.ts (1 hunks)
  • src/modules/orders/entities/order-item.entity.ts (0 hunks)
  • src/modules/orders/entities/order.entity.ts (0 hunks)
  • src/modules/products/products.module.ts (1 hunks)
  • src/modules/products/services/product.service.ts (3 hunks)
  • src/modules/reviews/controllers/review.controller.ts (2 hunks)
  • src/modules/reviews/dto/review.dto.ts (1 hunks)
  • src/modules/reviews/entities/review.entity.ts (1 hunks)
  • src/modules/reviews/services/review.service.ts (3 hunks)
  • src/modules/shared/middleware/auth.middleware.ts (3 hunks)
  • src/modules/shared/middleware/session.middleware.ts (1 hunks)
  • src/modules/shared/types/auth-request.type.ts (0 hunks)
  • src/modules/users/controllers/user.controller.ts (5 hunks)
  • src/modules/users/entities/user.entity.ts (1 hunks)
  • src/modules/users/services/user.service.ts (5 hunks)
  • src/modules/wishlist/common/types/auth-request.type.ts (0 hunks)
  • src/modules/wishlist/tests/wishlist.controller.spec.ts (4 hunks)
  • src/types/auth-request.type.ts (2 hunks)
  • src/types/express.d.ts (0 hunks)
  • src/types/global-response.type.ts (2 hunks)
  • tsconfig.json (1 hunks)
💤 Files with no reviewable changes (11)
  • src/types/express.d.ts
  • src/modules/files/tests/test-utils.ts
  • src/modules/files/tests/file.service.spec.ts
  • src/modules/auth/auth.module.ts
  • src/modules/orders/entities/order.entity.ts
  • src/modules/wishlist/common/types/auth-request.type.ts
  • src/modules/shared/types/auth-request.type.ts
  • src/modules/auth/index.ts
  • src/main.ts
  • src/modules/orders/entities/order-item.entity.ts
  • src/dtos/UserDTO.ts
🧰 Additional context used
📓 Path-based instructions (10)
src/**/*.ts

📄 CodeRabbit inference engine (.cursorrules)

src/**/*.ts: Do not access environment variables via process.env directly; import and use the config object from src/config/env
Provide explicit return types for all functions
Do not use the any type; use unknown or specific types instead
Create interfaces for complex object shapes
Type all function parameters explicitly
Do not use console.log; use proper logging (e.g., NestJS Logger)
Remove unused imports
Do not leave commented-out code in commits
Wrap risky operations in try-catch and handle errors appropriately
Add JSDoc comments for complex logic

Files:

  • src/modules/wishlist/tests/wishlist.controller.spec.ts
  • src/types/global-response.type.ts
  • src/modules/auth/controllers/role.controller.ts
  • src/common/filters/http-exception.filter.ts
  • src/modules/auth/middleware/authorize-roles.middleware.ts
  • src/modules/files/tests/file.controller.spec.ts
  • src/types/auth-request.type.ts
  • src/middleware/auth.middleware.ts
  • src/modules/reviews/controllers/review.controller.ts
  • src/config/index.ts
  • src/middleware/validateRequest.middleware.ts
  • src/middleware/userValidation.middleware.ts
  • src/modules/auth/strategies/jwt.strategy.ts
  • src/dtos/ProductVariantDTO.ts
  • src/modules/reviews/entities/review.entity.ts
  • src/middleware/paramValidation.middleware.ts
  • src/modules/auth/dto/auth.dto.ts
  • src/modules/auth/dto/auth-response.dto.ts
  • src/modules/auth/middleware/jwt-auth.middleware.ts
  • src/modules/products/products.module.ts
  • src/modules/shared/middleware/auth.middleware.ts
  • src/middleware/async-handler.ts
  • src/app.module.ts
  • src/modules/buyer-requests/services/buyer-requests.service.ts
  • src/modules/products/services/product.service.ts
  • src/modules/reviews/dto/review.dto.ts
  • src/modules/shared/middleware/session.middleware.ts
  • src/modules/notifications/dto/notification.dto.ts
  • src/modules/buyer-requests/entities/buyer-request.entity.ts
  • src/modules/users/controllers/user.controller.ts
  • src/modules/buyer-requests/dto/buyer-request-response.dto.ts
  • src/modules/auth/services/role.service.ts
  • src/modules/offers/services/offers.service.ts
  • src/modules/auth/entities/user-role.entity.ts
  • src/common/interceptors/response.interceptor.ts
  • src/modules/users/entities/user.entity.ts
  • src/modules/reviews/services/review.service.ts
  • src/modules/users/services/user.service.ts
  • src/modules/orders/dto/order.dto.ts
  • src/middleware/validation.middleware.ts
  • src/modules/auth/controllers/auth.controller.ts
  • src/modules/auth/tests/auth.service.spec.ts
  • src/modules/auth/services/auth.service.ts
src/**/*.spec.ts

📄 CodeRabbit inference engine (.cursorrules)

src/**/*.spec.ts: Create tests for new features
Mock external dependencies in tests
Test both success and error cases
Ensure tests are deterministic (no flakiness)

Files:

  • src/modules/wishlist/tests/wishlist.controller.spec.ts
  • src/modules/files/tests/file.controller.spec.ts
  • src/modules/auth/tests/auth.service.spec.ts
src/modules/**

📄 CodeRabbit inference engine (.cursorrules)

Follow NestJS module structure within src/modules/[module-name]/

Files:

  • src/modules/wishlist/tests/wishlist.controller.spec.ts
  • src/modules/auth/controllers/role.controller.ts
  • src/modules/auth/middleware/authorize-roles.middleware.ts
  • src/modules/files/tests/file.controller.spec.ts
  • src/modules/reviews/controllers/review.controller.ts
  • src/modules/auth/strategies/jwt.strategy.ts
  • src/modules/reviews/entities/review.entity.ts
  • src/modules/auth/dto/auth.dto.ts
  • src/modules/auth/dto/auth-response.dto.ts
  • src/modules/auth/middleware/jwt-auth.middleware.ts
  • src/modules/products/products.module.ts
  • src/modules/shared/middleware/auth.middleware.ts
  • src/modules/buyer-requests/services/buyer-requests.service.ts
  • src/modules/products/services/product.service.ts
  • src/modules/reviews/dto/review.dto.ts
  • src/modules/shared/middleware/session.middleware.ts
  • src/modules/notifications/dto/notification.dto.ts
  • src/modules/buyer-requests/entities/buyer-request.entity.ts
  • src/modules/users/controllers/user.controller.ts
  • src/modules/buyer-requests/dto/buyer-request-response.dto.ts
  • src/modules/auth/services/role.service.ts
  • src/modules/offers/services/offers.service.ts
  • src/modules/auth/entities/user-role.entity.ts
  • src/modules/users/entities/user.entity.ts
  • src/modules/reviews/services/review.service.ts
  • src/modules/users/services/user.service.ts
  • src/modules/orders/dto/order.dto.ts
  • src/modules/auth/controllers/auth.controller.ts
  • src/modules/auth/tests/auth.service.spec.ts
  • src/modules/auth/services/auth.service.ts
src/modules/**/tests/**/*.controller.spec.ts

📄 CodeRabbit inference engine (.cursorrules)

Include controller unit tests named [module].controller.spec.ts under tests/

Files:

  • src/modules/wishlist/tests/wishlist.controller.spec.ts
  • src/modules/files/tests/file.controller.spec.ts
src/modules/**/controllers/**/*.controller.ts

📄 CodeRabbit inference engine (.cursorrules)

Handle HTTP in controllers only

Files:

  • src/modules/auth/controllers/role.controller.ts
  • src/modules/reviews/controllers/review.controller.ts
  • src/modules/users/controllers/user.controller.ts
  • src/modules/auth/controllers/auth.controller.ts
src/modules/**/entities/**/*.entity.ts

📄 CodeRabbit inference engine (.cursorrules)

Define persistence entities in entities/*.entity.ts

Files:

  • src/modules/reviews/entities/review.entity.ts
  • src/modules/buyer-requests/entities/buyer-request.entity.ts
  • src/modules/auth/entities/user-role.entity.ts
  • src/modules/users/entities/user.entity.ts
src/modules/**/dto/**/*.dto.ts

📄 CodeRabbit inference engine (.cursorrules)

src/modules/**/dto/**/*.dto.ts: Use DTOs for data validation
Use class-validator decorators on DTOs

Files:

  • src/modules/auth/dto/auth.dto.ts
  • src/modules/auth/dto/auth-response.dto.ts
  • src/modules/reviews/dto/review.dto.ts
  • src/modules/notifications/dto/notification.dto.ts
  • src/modules/buyer-requests/dto/buyer-request-response.dto.ts
  • src/modules/orders/dto/order.dto.ts
src/modules/**/[a-zA-Z0-9-]*.module.ts

📄 CodeRabbit inference engine (.cursorrules)

Provide a module file per feature module (e.g., [module].module.ts)

Files:

  • src/modules/products/products.module.ts
src/modules/**/services/**/*.service.ts

📄 CodeRabbit inference engine (.cursorrules)

Place business logic in services

Files:

  • src/modules/buyer-requests/services/buyer-requests.service.ts
  • src/modules/products/services/product.service.ts
  • src/modules/auth/services/role.service.ts
  • src/modules/offers/services/offers.service.ts
  • src/modules/reviews/services/review.service.ts
  • src/modules/users/services/user.service.ts
  • src/modules/auth/services/auth.service.ts
src/modules/**/tests/**/*.service.spec.ts

📄 CodeRabbit inference engine (.cursorrules)

Include service unit tests named [module].service.spec.ts under tests/

Files:

  • src/modules/auth/tests/auth.service.spec.ts
🧠 Learnings (3)
📚 Learning: 2025-09-29T16:06:54.363Z
Learnt from: CR
PR: StarShopCr/StarShop-Backend#0
File: .cursorrules:0-0
Timestamp: 2025-09-29T16:06:54.363Z
Learning: Applies to src/modules/**/dto/**/*.dto.ts : Use DTOs for data validation

Applied to files:

  • src/middleware/userValidation.middleware.ts
  • src/modules/auth/dto/auth.dto.ts
📚 Learning: 2025-09-29T16:06:54.363Z
Learnt from: CR
PR: StarShopCr/StarShop-Backend#0
File: .cursorrules:0-0
Timestamp: 2025-09-29T16:06:54.363Z
Learning: Applies to src/modules/**/dto/**/*.dto.ts : Use class-validator decorators on DTOs

Applied to files:

  • src/middleware/userValidation.middleware.ts
  • src/modules/auth/dto/auth.dto.ts
  • src/middleware/validation.middleware.ts
📚 Learning: 2025-09-29T16:06:54.363Z
Learnt from: CR
PR: StarShopCr/StarShop-Backend#0
File: .cursorrules:0-0
Timestamp: 2025-09-29T16:06:54.363Z
Learning: Ensure code follows NestJS patterns and is production-ready per the quality checklist

Applied to files:

  • README.md
🧬 Code graph analysis (7)
src/middleware/validateRequest.middleware.ts (1)
src/middleware/validation.middleware.ts (1)
  • validateRequest (7-22)
src/middleware/paramValidation.middleware.ts (1)
src/middleware/validateRequest.middleware.ts (1)
  • validateRequest (8-17)
src/modules/buyer-requests/services/buyer-requests.service.ts (2)
src/modules/buyer-requests/dto/create-buyer-request.dto.ts (1)
  • CreateBuyerRequestDto (14-54)
src/modules/buyer-requests/dto/buyer-request-response.dto.ts (1)
  • BuyerRequestResponseDto (3-22)
src/modules/users/controllers/user.controller.ts (4)
src/modules/wishlist/controller/wishlist.controller.ts (1)
  • UseGuards (16-47)
src/modules/auth/dto/auth.dto.ts (1)
  • UpdateUserDto (54-70)
src/dtos/UserDTO.ts (1)
  • UpdateUserDto (30-43)
src/utils/errors.ts (1)
  • UnauthorizedError (18-22)
src/modules/auth/services/role.service.ts (1)
src/modules/auth/types/role.types.ts (1)
  • RoleName (1-1)
src/middleware/validation.middleware.ts (1)
src/middleware/validateRequest.middleware.ts (1)
  • validateRequest (8-17)
src/modules/auth/services/auth.service.ts (2)
src/config/index.ts (1)
  • config (1-29)
src/modules/auth/types/role.types.ts (1)
  • RoleName (1-1)

Comment on lines +7 to +9
POSTGRES_USER: user
POSTGRES_PASSWORD: password
POSTGRES_DB: starshop
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

Security risk: hardcoded credentials in version control.

The PostgreSQL credentials are hardcoded with weak values (user/password). This poses a security risk if the compose file is used in non-local environments or committed to public repositories.

Apply this diff to use environment variables with safe defaults:

     environment:
-      POSTGRES_USER: user
-      POSTGRES_PASSWORD: password
-      POSTGRES_DB: starshop
+      POSTGRES_USER: ${POSTGRES_USER:-user}
+      POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:-password}
+      POSTGRES_DB: ${POSTGRES_DB:-starshop}

Then create a .env.example file with placeholders and add .env to .gitignore.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
POSTGRES_USER: user
POSTGRES_PASSWORD: password
POSTGRES_DB: starshop
environment:
- POSTGRES_USER: user
- POSTGRES_PASSWORD: password
POSTGRES_USER: ${POSTGRES_USER:-user}
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:-password}
POSTGRES_DB: ${POSTGRES_DB:-starshop}
🤖 Prompt for AI Agents
In docker-compose.yml around lines 7 to 9, the PostgreSQL credentials are
hardcoded (POSTGRES_USER/POSTGRES_PASSWORD/POSTGRES_DB) which is a security
risk; change these to reference environment variables (e.g., POSTGRES_USER,
POSTGRES_PASSWORD, POSTGRES_DB) with safe defaults using parameter expansion,
update any docs to instruct using a .env file, add a new .env.example with
placeholder values for these variables (do not include real secrets), and add
.env to .gitignore so runtime secrets are not committed to version control.

Milestone,
],
synchronize: false,
synchronize: process.env.NODE_ENV !== 'production',
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

Critical: Dynamic synchronize poses data loss risk.

Similar to the issue in src/config/index.ts, enabling synchronize: process.env.NODE_ENV !== 'production' will cause TypeORM to automatically alter the database schema in all non-production environments, risking data loss and making it difficult to test migrations.

This setting should be false in all environments, with explicit migrations handling schema changes:

-      synchronize: process.env.NODE_ENV !== 'production',
+      synchronize: false,

If this change was intentional, ensure:

  1. All developers understand the risk
  2. Staging uses NODE_ENV=production or a separate config
  3. Documentation warns about potential data loss
  4. Migration strategy is planned before production deployment
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
synchronize: process.env.NODE_ENV !== 'production',
synchronize: false,
🤖 Prompt for AI Agents
In src/app.module.ts around line 67, the TypeORM config uses synchronize:
process.env.NODE_ENV !== 'production', which risks automatic schema changes and
data loss; change this to always set synchronize: false and ensure migrations
are applied explicitly (use TypeORM migrations runner or your migration
service), update any environment/config logic to drive migrations instead of
schema sync, and add/adjust docs and dev onboarding to explain that schema
changes must be performed via migrations and how to run them (ensure staging
uses safe env/config).


@Injectable()
export class ResponseInterceptor implements NestInterceptor {
intercept(context: ExecutionContext, next: CallHandler): Observable<any> {
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion | 🟠 Major

Replace Observable<any> with a specific type.

The return type Observable<any> violates coding guidelines. Define an interface for the response format.

As per coding guidelines.

Apply this diff to add a response interface:

+interface StandardResponse<T = any> {
+  success: boolean;
+  data?: T;
+  token?: string;
+  message?: string;
+}
+
 @Injectable()
 export class ResponseInterceptor implements NestInterceptor {
-  intercept(context: ExecutionContext, next: CallHandler): Observable<any> {
+  intercept(context: ExecutionContext, next: CallHandler): Observable<StandardResponse> {
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
intercept(context: ExecutionContext, next: CallHandler): Observable<any> {
import { Injectable, NestInterceptor, ExecutionContext, CallHandler } from '@nestjs/common';
import { Observable } from 'rxjs';
interface StandardResponse<T = any> {
success: boolean;
data?: T;
token?: string;
message?: string;
}
@Injectable()
export class ResponseInterceptor implements NestInterceptor {
intercept(context: ExecutionContext, next: CallHandler): Observable<StandardResponse> {
// existing implementation...
}
}
🤖 Prompt for AI Agents
In src/common/interceptors/response.interceptor.ts around line 13, the method
signature uses the generic Observable<any>; define a concrete response interface
(e.g., a Response<T> with fields like data, status, message or whatever the
project standard requires) in this file or a shared types file, import it, and
replace Observable<any> with Observable<Response<any>> or make the interceptor
generic (Observable<Response<T>>) if appropriate; update the intercept
implementation/return mapping to build and return objects matching the new
interface and add any necessary imports/exports.

password: process.env.DB_PASSWORD || 'password',
name: process.env.DB_DATABASE || 'starshop',
synchronize: false,
synchronize: process.env.NODE_ENV !== 'production',
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

Critical: Dynamic synchronize in non-production poses data loss risk.

Changing synchronize from false to process.env.NODE_ENV !== 'production' enables TypeORM auto-synchronization in development, staging, and any non-production environment. This will automatically alter the database schema on application startup to match entity definitions, which can lead to:

  • Data loss if columns are removed or renamed
  • Unintended schema changes in shared development/staging databases
  • Difficulty reproducing production-like conditions in staging

Consider using explicit database migrations instead of auto-sync, even in development:

-    synchronize: process.env.NODE_ENV !== 'production',
+    synchronize: false,

If auto-sync is intentional for local development only, document this clearly and ensure staging environments use NODE_ENV=production or have a separate configuration.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
synchronize: process.env.NODE_ENV !== 'production',
synchronize: false,
🤖 Prompt for AI Agents
In src/config/index.ts around line 10, the synchronize setting is currently set
to process.env.NODE_ENV !== 'production', which enables TypeORM auto-schema sync
in non-production and risks data loss; change this to a safe default
(synchronize: false), remove auto-sync for staging, and instead drive schema
changes with explicit migrations; if you still want auto-sync for local dev
only, make it conditional on a dedicated env var (e.g., TYPEORM_SYNC=true) and
document that local-only behavior and ensure staging uses NODE_ENV=production or
TYPEORM_SYNC unset.

Comment on lines +1 to +5
export interface AttributeValueDTO {
attributeId: string; // UUID or numeric depending on schema
valueId: string; // references attribute value entity
value?: string; // optional human-readable value
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

ID types inconsistent with PR migration to numeric IDs.

The comment mentions "UUID or numeric depending on schema," but attributeId and valueId are typed as string. According to the PR objectives, IDs are being migrated from UUID/string to numeric across DTOs and entities. These fields should be typed as number to align with the migration.

Apply this diff to fix the type inconsistency:

 export interface AttributeValueDTO {
-  attributeId: string; // UUID or numeric depending on schema
-  valueId: string; // references attribute value entity
+  attributeId: number;
+  valueId: number;
   value?: string; // optional human-readable value
 }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
export interface AttributeValueDTO {
attributeId: string; // UUID or numeric depending on schema
valueId: string; // references attribute value entity
value?: string; // optional human-readable value
}
export interface AttributeValueDTO {
attributeId: number;
valueId: number;
value?: string; // optional human-readable value
}
🤖 Prompt for AI Agents
In src/dtos/ProductVariantDTO.ts around lines 1 to 5, the AttributeValueDTO
fields attributeId and valueId are typed as string while the project is
migrating IDs to numeric; change attributeId and valueId types from string to
number, keep value as optional string, and update any related
imports/usages/tests to accept numeric IDs to maintain type consistency across
DTOs and entities.

Comment on lines +16 to 19
const userId = Number(req.user.id);
if (!userId) {
throw new BadRequestError('User ID is required');
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

Incorrect validation logic for numeric user ID.

Using Number() coercion followed by a falsy check is problematic:

  • Number("0") returns 0, which is falsy, so a valid user ID of 0 would incorrectly throw "User ID is required"
  • Number("invalid") returns NaN, which is truthy, so invalid input would pass this check and cause issues downstream

Apply this diff to properly validate the numeric ID:

-      const userId = Number(req.user.id);
-      if (!userId) {
+      const userId = Number(req.user.id);
+      if (isNaN(userId)) {
         throw new BadRequestError('User ID is required');
       }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const userId = Number(req.user.id);
if (!userId) {
throw new BadRequestError('User ID is required');
}
const userId = Number(req.user.id);
if (isNaN(userId)) {
throw new BadRequestError('User ID is required');
}
🤖 Prompt for AI Agents
In src/modules/reviews/controllers/review.controller.ts around lines 16-19, the
current Number() coercion plus a falsy check wrongly rejects valid 0 IDs and
lets NaN pass; replace it with explicit numeric validation: convert req.user.id
to a number (or use parseInt) and then check Number.isInteger(userId) (and
optionally userId >= 0) or use Number.isFinite and not NaN; if the check fails
throw BadRequestError('User ID is required' or 'Invalid user ID').

Comment on lines +81 to 84
const userId = Number(req.user.id);
if (!userId) {
throw new BadRequestError('User ID is required');
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

Incorrect validation logic for numeric user ID (duplicate issue).

Same validation issue as in createReview: Number() coercion with falsy check will fail for user ID 0 and pass for NaN.

Apply this diff:

-      const userId = Number(req.user.id);
-      if (!userId) {
+      const userId = Number(req.user.id);
+      if (isNaN(userId)) {
         throw new BadRequestError('User ID is required');
       }
🤖 Prompt for AI Agents
In src/modules/reviews/controllers/review.controller.ts around lines 81 to 84,
the code uses Number(req.user.id) with a falsy check which rejects valid userId
0 and may accept NaN; replace the validation with a proper numeric check such
as: convert to number, then verify it's a finite integer (e.g.,
Number.isInteger(userId) or !Number.isNaN(userId) && isFinite(userId)) and only
throw BadRequestError if the check fails, so 0 is allowed and non-numeric values
are rejected.

Comment on lines 16 to +20
@PrimaryGeneratedColumn('uuid')
id: string;

@Column({ name: 'userId', type: 'uuid' })
userId: string;
@Column({ name: 'userId' })
userId: number;
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Inconsistent primary key strategy: UUID for Review but numeric foreign keys.

The Review entity uses @PrimaryGeneratedColumn('uuid') for its own id (line 16) but now references numeric userId (line 20). This mixed approach can complicate:

  • Indexing and join performance (UUID vs numeric)
  • API consistency (some IDs are UUIDs, others are numbers)
  • Migration complexity

Consider standardizing on either all numeric IDs or all UUIDs across entities for consistency.

🤖 Prompt for AI Agents
In src/modules/reviews/entities/review.entity.ts around lines 16 to 20, the
Review entity currently uses a UUID primary key but a numeric userId foreign key
which creates inconsistent ID strategies; fix by choosing one ID strategy and
making types consistent: either switch the Review.id to a numeric
@PrimaryGeneratedColumn() to match numeric userId, or change userId to a UUID
string (e.g., @Column({ type: 'uuid', name: 'userId' }) userId: string) and
update the relation decorator to reference the User entity's uuid PK. After
changing types, update the TypeORM relation definitions, adjust any
DTOs/services/controllers that assume number vs string, regenerate or add DB
migrations for the altered column type, and run tests to ensure joins and APIs
use the consistent ID format.

Comment on lines +19 to +20
@Column({ name: 'userId' })
userId: number;
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

Verify database migration for userId type change and unique constraint.

The userId column type changed from UUID to numeric, which impacts:

  1. The existing @Unique(['userId', 'productId']) constraint (line 14) must be dropped and recreated
  2. Existing foreign key relationships need updating
  3. Data migration is required if the table contains records

Run the following script to check for related migrations and verify entity key types:


🏁 Script executed:

#!/bin/bash
# Description: Verify migration files and entity key types for reviews

# Check for review-related migration files
echo "=== Review Migration Files ==="
fd -e ts -e js migration | xargs rg -l "review" -i

# Verify User and Product primary key types
echo -e "\n=== User Entity Primary Key ==="
rg -A2 "class User" src/modules/users/entities/user.entity.ts | rg -A1 "@PrimaryGeneratedColumn"

echo -e "\n=== Product Entity Primary Key ==="
rg -A2 "class Product" src/modules/products/entities/product.entity.ts | rg -A1 "@PrimaryGeneratedColumn"

Length of output: 183


Add missing DB migration for review entity type changes and constraints

  • Create a migration to alter review.userId and review.productId from UUID to integer.
  • Drop and recreate the UNIQUE(userId, productId) index.
  • Update foreign key definitions to reference the numeric users.id and products.id.
  • Include data migration logic to convert existing UUID references to their integer counterparts.

Comment on lines +109 to 115
const currentUserId = req.user?.id;
const currentUserRole = req.user?.role?.[0];

// Check if user is updating their own profile or is admin
if (walletAddress !== currentUserWalletAddress && currentUserRole !== 'admin') {
if (userId !== currentUserId && currentUserRole !== 'admin') {
throw new UnauthorizedError('You can only update your own profile');
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

Verify req.user.id type consistency.

The code compares userId (number from ParseIntPipe) with currentUserId (from req.user?.id). Ensure that the JWT payload and request typing define req.user.id as number to prevent type coercion issues in the strict equality check.

Run the following script to check the AuthRequest interface and user type definitions:


🏁 Script executed:

#!/bin/bash
# Description: Verify req.user.id is typed as number in AuthRequest/JWT payload

# Search for AuthRequest interface definition
ast-grep --pattern $'interface AuthRequest {
  $$$
}'

# Search for Request user property extensions
rg -nP --type=ts -C5 'interface.*Request|declare.*Express' src/

Length of output: 8210


Convert req.user.id to a number before strict comparison
req.user.id is typed as string | number (and in middleware as string), so comparing it directly to the numeric userId will always fail. Cast it to a number (e.g.

const currentUserId = Number(req.user?.id);

) or enforce a numeric id in your JWT payload.

🤖 Prompt for AI Agents
In src/modules/users/controllers/user.controller.ts around lines 109 to 115, the
code compares numeric userId to req.user.id which can be a string, so the strict
comparison will fail; convert req.user.id to a number before comparing (e.g.
assign currentUserId = Number(req.user?.id)), validate that conversion (handle
NaN by treating as unauthorized or throwing a BadRequest/UnauthorizedError),
then use the numeric currentUserId in the existing ownership/admin check so the
comparison works correctly.

@manzano-bjf manzano-bjf closed this by deleting the head repository Oct 11, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant

Comments