From b4ace2f1b58adf805c144b3c6056dc5268bcd6ec Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E2=80=9COmarNabil005=E2=80=9D?= Date: Mon, 15 Dec 2025 19:26:26 +0200 Subject: [PATCH] fixed more maintainability issues --- src/auth/auth.service.ts | 2 +- .../services/password/password.service.ts | 6 ++- src/messages/adapters/ws-auth.adapter.ts | 2 +- src/post/post.controller.ts | 7 +-- .../services/personalized-trends.service.ts | 2 +- src/post/services/post.service.ts | 46 ++++++++++--------- src/post/services/redis-trending.service.ts | 6 +-- src/post/services/repost.service.ts | 2 +- src/profile/profile.controller.ts | 1 - src/profile/profile.service.ts | 12 +++-- src/storage/storage.service.ts | 10 ++-- src/user/user.service.ts | 1 - src/users/users.service.ts | 6 +-- src/utils/otp.util.ts | 2 +- 14 files changed, 54 insertions(+), 51 deletions(-) diff --git a/src/auth/auth.service.ts b/src/auth/auth.service.ts index b2271b9..bbf7505 100644 --- a/src/auth/auth.service.ts +++ b/src/auth/auth.service.ts @@ -201,7 +201,7 @@ export class AuthService { const userId = 'sub' in user ? user.sub : user.id; const { accessToken, ...result } = await this.login(userId, user.username); return { accessToken, result }; - } catch (error) { + } catch { throw new UnauthorizedException('Invalid Google ID token'); } } diff --git a/src/auth/services/password/password.service.ts b/src/auth/services/password/password.service.ts index 0c01efb..c3ac969 100644 --- a/src/auth/services/password/password.service.ts +++ b/src/auth/services/password/password.service.ts @@ -72,10 +72,12 @@ export class PasswordService { await this.redisService.set(redisKey, tokenHash, RESET_TOKEN_TTL_SECONDS); await this.incrementResetAttempts(email); await this.redisService.set(cooldownKey, 'true', PASSWORD_RESET_COOLDOWN_SECONDS); + const backendBaseUrl = process.env.NODE_ENV === 'dev' ? process.env.BACKEND_URL_DEV : process.env.BACKEND_URL_PROD; + const frontendBaseUrl = process.env.NODE_ENV === 'dev' ? process.env.FRONTEND_URL : process.env.FRONTEND_URL_PROD; const resetUrl = requestPasswordResetDto.type === RequestType.MOBILE - ? `${process.env.NODE_ENV === 'dev' ? process.env.BACKEND_URL_DEV : process.env.BACKEND_URL_PROD}/api/${process.env.APP_VERSION}/auth/reset-mobile-password?token=${resetToken}&id=${user.id}` - : `${process.env.NODE_ENV === 'dev' ? process.env.FRONTEND_URL : process.env.FRONTEND_URL_PROD}/reset-password?token=${resetToken}&id=${user.id}`; + ? `${backendBaseUrl}/api/${process.env.APP_VERSION}/auth/reset-mobile-password?token=${resetToken}&id=${user.id}` + : `${frontendBaseUrl}/reset-password?token=${resetToken}&id=${user.id}`; await this.emailService.queueTemplateEmail( [email], diff --git a/src/messages/adapters/ws-auth.adapter.ts b/src/messages/adapters/ws-auth.adapter.ts index 16b8438..998b951 100644 --- a/src/messages/adapters/ws-auth.adapter.ts +++ b/src/messages/adapters/ws-auth.adapter.ts @@ -61,7 +61,7 @@ export class AuthenticatedSocketAdapter extends IoAdapter { socket.data.username = payload.username; next(); - } catch (error) { + } catch { next(new Error('Invalid authentication token')); } }); diff --git a/src/post/post.controller.ts b/src/post/post.controller.ts index bfc4157..90b0c1a 100644 --- a/src/post/post.controller.ts +++ b/src/post/post.controller.ts @@ -3,14 +3,11 @@ import { Body, Controller, Delete, - FileTypeValidator, Get, HttpStatus, Inject, - MaxFileSizeValidator, Param, ParseArrayPipe, - ParseFilePipe, Post, Query, UploadedFiles, @@ -43,12 +40,10 @@ import { GetLikedPostsResponseDto, } from './dto/like-response.dto'; import { ToggleRepostResponseDto, GetRepostersResponseDto } from './dto/repost-response.dto'; -import { SearchByHashtagResponseDto } from './dto/hashtag-search-response.dto'; import { SearchPostsResponseDto } from './dto/search-response.dto'; import { GetPostStatsResponseDto } from './dto/post-stats-response.dto'; import { ErrorResponseDto } from 'src/common/dto/error-response.dto'; import { JwtAuthGuard } from 'src/auth/guards/jwt-auth/jwt-auth.guard'; - import { AuthenticatedUser } from 'src/auth/interfaces/user.interface'; import { CurrentUser } from 'src/auth/decorators/current-user.decorator'; import { PostFiltersDto } from './dto/post-filter.dto'; @@ -56,7 +51,7 @@ import { SearchPostsDto } from './dto/search-posts.dto'; import { SearchByHashtagDto } from './dto/search-by-hashtag.dto'; import { MentionService } from './services/mention.service'; import { ApiResponseDto } from 'src/common/dto/base-api-response.dto'; -import { Mention, Post as PostModel, PostVisibility, User } from '@prisma/client'; +import { Post as PostModel, User } from '@prisma/client'; import { FilesInterceptor } from '@nestjs/platform-express'; import { ImageVideoUploadPipe } from 'src/storage/pipes/file-upload.pipe'; import { TimelineFeedResponseDto } from './dto/timeline-feed-reponse.dto'; diff --git a/src/post/services/personalized-trends.service.ts b/src/post/services/personalized-trends.service.ts index 83b820d..2ba9192 100644 --- a/src/post/services/personalized-trends.service.ts +++ b/src/post/services/personalized-trends.service.ts @@ -193,7 +193,7 @@ export class PersonalizedTrendsService { } if (userCategories.includes(category)) { - return 1.0; + return 1; } return 0.3; diff --git a/src/post/services/post.service.ts b/src/post/services/post.service.ts index 799f094..5f0cef0 100644 --- a/src/post/services/post.service.ts +++ b/src/post/services/post.service.ts @@ -1206,7 +1206,9 @@ export class PostService { ); const postMap = new Map(); - enrichedOriginalParentData.forEach((p) => postMap.set(p.postId, p)); + for (const p of enrichedOriginalParentData) { + postMap.set(p.postId, p); + } // 5. Embed original post data into reposts return reposts.map((r) => ({ @@ -1549,12 +1551,12 @@ private async GetPersonalizedForYouPosts( console.log(`[QUERY] Starting ULTRA-OPTIMIZED GetPersonalizedForYouPosts for user ${userId}`); const personalizationWeights = { - ownPost: 20.0, - following: 15.0, - directLike: 10.0, - commonLike: 5.0, - commonFollow: 3.0, - wTypePost: 1.0, + ownPost: 20, + following: 15, + directLike: 10, + commonLike: 5, + commonFollow: 3, + wTypePost: 1, wTypeQuote: 0.8, wTypeRepost: 0.5, }; @@ -1915,8 +1917,8 @@ private async GetPersonalizedForYouPosts( const wReplies = 0.15; const wMentions = 0.1; const wFreshness = 0.1; - const T = 2.0; - const wTypePost = 1.0; + const T = 2; + const wTypePost = 1; const wTypeQuote = 0.8; const wTypeRepost = 0.5; @@ -2441,12 +2443,12 @@ private async GetPersonalizedForYouPosts( ): Promise { const { page = 1, limit = 50, sortBy = 'score' } = options; const personalizationWeights = { - ownPost: 20.0, // NEW: Bonus for user's own posts - following: 15.0, - directLike: 10.0, - commonLike: 5.0, - commonFollow: 3.0, - wTypePost: 1.0, + ownPost: 20, // NEW: Bonus for user's own posts + following: 15, + directLike: 10, + commonLike: 5, + commonFollow: 3, + wTypePost: 1, wTypeQuote: 0.8, }; @@ -2457,7 +2459,7 @@ private async GetPersonalizedForYouPosts( // Escape and format interest names for SQL IN clause const escapedInterestNames = interestNames - .map((name) => `'${name.replaceAll(/'/g, "''")}'`) + .map((name) => `'${name.replaceAll('\'', '\'\'')}'`) .join(', '); const query = ` @@ -2853,12 +2855,12 @@ private async GetPersonalizedForYouPosts( sortBy: 'score' | 'latest', ): Promise { const personalizationWeights = { - ownPost: 20.0, - following: 15.0, - directLike: 10.0, - commonLike: 5.0, - commonFollow: 3.0, - wTypePost: 1.0, + ownPost: 20, + following: 15, + directLike: 10, + commonLike: 5, + commonFollow: 3, + wTypePost: 1, wTypeQuote: 0.8, }; diff --git a/src/post/services/redis-trending.service.ts b/src/post/services/redis-trending.service.ts index df265ef..88193cd 100644 --- a/src/post/services/redis-trending.service.ts +++ b/src/post/services/redis-trending.service.ts @@ -34,8 +34,8 @@ export class RedisTrendingService { private readonly COUNTS_CACHE_TTL = 300; // 5 minutes // Lazy update queue - private updateQueue = new Map>(); - private updateTimers = new Map(); + private readonly updateQueue = new Map>(); + private readonly updateTimers = new Map(); constructor( @Inject(Services.REDIS) @@ -111,7 +111,7 @@ export class RedisTrendingService { this.updateQueue.get(queueKey)!.add(hashtagId); if (this.updateTimers.has(queueKey)) { - clearTimeout(this.updateTimers.get(queueKey)!); + clearTimeout(this.updateTimers.get(queueKey)); } const timer = setTimeout(() => { diff --git a/src/post/services/repost.service.ts b/src/post/services/repost.service.ts index 2cdfaf1..5026018 100644 --- a/src/post/services/repost.service.ts +++ b/src/post/services/repost.service.ts @@ -1,4 +1,4 @@ -import { Inject, Injectable, NotFoundException, forwardRef } from '@nestjs/common'; +import { Inject, Injectable, forwardRef } from '@nestjs/common'; import { PrismaService } from 'src/prisma/prisma.service'; import { Services } from 'src/utils/constants'; import { EventEmitter2 } from '@nestjs/event-emitter'; diff --git a/src/profile/profile.controller.ts b/src/profile/profile.controller.ts index 0cac995..d1f121d 100644 --- a/src/profile/profile.controller.ts +++ b/src/profile/profile.controller.ts @@ -41,7 +41,6 @@ import { OptionalJwtAuthGuard } from '../auth/guards/optional-jwt-auth/optional- import { CurrentUser } from '../auth/decorators/current-user.decorator'; import { Routes, Services } from 'src/utils/constants'; import { ErrorResponseDto } from 'src/common/dto/error-response.dto'; -import { Public } from 'src/auth/decorators/public.decorator'; @ApiTags('Profile') @Controller(Routes.PROFILE) diff --git a/src/profile/profile.service.ts b/src/profile/profile.service.ts index 9033f63..c0feacc 100644 --- a/src/profile/profile.service.ts +++ b/src/profile/profile.service.ts @@ -416,7 +416,9 @@ export class ProfileService { followingId: true, }, }); - followRelations.forEach((rel) => followStatusMap.set(rel.followingId, true)); + for (const rel of followRelations) { + followStatusMap.set(rel.followingId, true); + } // Batch check if profile users follow current user const followingMeRelations = await this.prismaService.follow.findMany({ @@ -430,7 +432,9 @@ export class ProfileService { followerId: true, }, }); - followingMeRelations.forEach((rel) => followingMeStatusMap.set(rel.followerId, true)); + for (const rel of followingMeRelations) { + followingMeStatusMap.set(rel.followerId, true); + } // Batch check mute status const muteRelations = await this.prismaService.mute.findMany({ @@ -444,7 +448,9 @@ export class ProfileService { mutedId: true, }, }); - muteRelations.forEach((rel) => muteStatusMap.set(rel.mutedId, true)); + for (const rel of muteRelations) { + muteStatusMap.set(rel.mutedId, true); + } } const profilesWithCounts = profiles.map((profile) => { diff --git a/src/storage/storage.service.ts b/src/storage/storage.service.ts index 05940d9..e8dd199 100644 --- a/src/storage/storage.service.ts +++ b/src/storage/storage.service.ts @@ -6,16 +6,16 @@ import { HeadObjectCommand, } from '@aws-sdk/client-s3'; import { ConfigService } from '@nestjs/config'; -import { extname } from 'path'; +import { extname } from 'node:path'; import { v4 as uuid } from 'uuid'; @Injectable() export class StorageService { - private s3Client: S3Client; - private bucketName: string; - private region: string; + private readonly s3Client: S3Client; + private readonly bucketName: string; + private readonly region: string; - constructor(private configService: ConfigService) { + constructor(private readonly configService: ConfigService) { this.bucketName = this.configService.get('AWS_S3_BUCKET_NAME') || 'hankers-uploads-prod'; this.region = this.configService.get('AWS_REGION') || 'us-east-1'; diff --git a/src/user/user.service.ts b/src/user/user.service.ts index e3443b4..4c96cda 100644 --- a/src/user/user.service.ts +++ b/src/user/user.service.ts @@ -2,7 +2,6 @@ import { Inject, Injectable } from '@nestjs/common'; import { PrismaService } from '../prisma/prisma.service'; import { CreateUserDto } from './dto/create-user.dto'; import { hash } from 'argon2'; -import { UpdateUserDto } from './dto/update-user.dto'; import { Services } from 'src/utils/constants'; import { OAuthProfileDto } from 'src/auth/dto/oauth-profile.dto'; import { generateUsername } from 'src/utils/username.util'; diff --git a/src/users/users.service.ts b/src/users/users.service.ts index a29d979..3319e52 100644 --- a/src/users/users.service.ts +++ b/src/users/users.service.ts @@ -356,7 +356,7 @@ export class UsersService { limit, ); - const totalItemsResult = (await this.prismaService.$queryRawUnsafe( + const totalItemsResult = await this.prismaService.$queryRawUnsafe<{ count: string }[]>( ` SELECT COUNT(*) AS count FROM "follows" f @@ -366,8 +366,8 @@ export class UsersService { `, userId, authenticatedUserId, - )) as any[]; - const totalItems = Number.parseInt((totalItemsResult[0] as any).count, 10); + ); + const totalItems = Number.parseInt(totalItemsResult[0].count, 10); const metadata = { totalItems, diff --git a/src/utils/otp.util.ts b/src/utils/otp.util.ts index b086af3..b57c69c 100644 --- a/src/utils/otp.util.ts +++ b/src/utils/otp.util.ts @@ -1,4 +1,4 @@ -import * as crypto from 'crypto'; +import * as crypto from 'node:crypto'; export function generateOtp(size: number = 6): string { const max = Math.pow(10, size);