Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/auth/auth.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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');
}
}
Expand Down
6 changes: 4 additions & 2 deletions src/auth/services/password/password.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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],
Expand Down
2 changes: 1 addition & 1 deletion src/messages/adapters/ws-auth.adapter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ export class AuthenticatedSocketAdapter extends IoAdapter {
socket.data.username = payload.username;

next();
} catch (error) {
} catch {
next(new Error('Invalid authentication token'));
}
});
Expand Down
7 changes: 1 addition & 6 deletions src/post/post.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,11 @@ import {
Body,
Controller,
Delete,
FileTypeValidator,
Get,
HttpStatus,
Inject,
MaxFileSizeValidator,
Param,
ParseArrayPipe,
ParseFilePipe,
Post,
Query,
UploadedFiles,
Expand Down Expand Up @@ -43,20 +40,18 @@ 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';
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';
Expand Down
2 changes: 1 addition & 1 deletion src/post/services/personalized-trends.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -193,7 +193,7 @@ export class PersonalizedTrendsService {
}

if (userCategories.includes(category)) {
return 1.0;
return 1;
}

return 0.3;
Expand Down
68 changes: 35 additions & 33 deletions src/post/services/post.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1218,7 +1218,9 @@ export class PostService {
);

const postMap = new Map<number, any>();
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 {
Expand Down Expand Up @@ -1581,23 +1583,23 @@ export class PostService {

return { posts: formattedPosts };
}
private async GetPersonalizedForYouPosts(
userId: number,
page = 1,
limit = 50,
): Promise<PostWithAllData[]> {
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,
wTypeQuote: 0.8,
wTypeRepost: 0.5,
};
private async GetPersonalizedForYouPosts(
userId: number,
page = 1,
limit = 50,
): Promise<PostWithAllData[]> {
console.log(`[QUERY] Starting ULTRA-OPTIMIZED GetPersonalizedForYouPosts for user ${userId}`);
const personalizationWeights = {
ownPost: 20,
following: 15,
directLike: 10,
commonLike: 5,
commonFollow: 3,
wTypePost: 1,
wTypeQuote: 0.8,
wTypeRepost: 0.5,
};

// KEY OPTIMIZATION: Instead of pulling ALL posts from ALL interests,
// we'll pull TOP posts from EACH interest, then combine and re-rank
Expand Down Expand Up @@ -1942,8 +1944,8 @@ export class PostService {
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;

Expand Down Expand Up @@ -2458,12 +2460,12 @@ export class PostService {
): Promise<PostWithAllData[]> {
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,
};

Expand All @@ -2474,7 +2476,7 @@ export class PostService {

// Escape and format interest names for SQL IN clause
const escapedInterestNames = interestNames
.map((name) => `'${name.replaceAll(/'/g, "''")}'`)
.map((name) => `'${name.replaceAll('\'', '\'\'')}'`)
.join(', ');

const query = `
Expand Down Expand Up @@ -2849,12 +2851,12 @@ export class PostService {
sortBy: 'score' | 'latest',
): Promise<PostWithInterestName[]> {
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,
};

Expand Down
6 changes: 3 additions & 3 deletions src/post/services/redis-trending.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,8 +34,8 @@ export class RedisTrendingService {
private readonly COUNTS_CACHE_TTL = 300; // 5 minutes

// Lazy update queue
private updateQueue = new Map<string, Set<number>>();
private updateTimers = new Map<string, NodeJS.Timeout>();
private readonly updateQueue = new Map<string, Set<number>>();
private readonly updateTimers = new Map<string, NodeJS.Timeout>();

constructor(
@Inject(Services.REDIS)
Expand Down Expand Up @@ -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(() => {
Expand Down
2 changes: 1 addition & 1 deletion src/post/services/repost.service.ts
Original file line number Diff line number Diff line change
@@ -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';
Expand Down
1 change: 0 additions & 1 deletion src/profile/profile.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
12 changes: 9 additions & 3 deletions src/profile/profile.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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({
Expand All @@ -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({
Expand All @@ -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) => {
Expand Down
10 changes: 5 additions & 5 deletions src/storage/storage.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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<string>('AWS_S3_BUCKET_NAME') || 'hankers-uploads-prod';
this.region = this.configService.get<string>('AWS_REGION') || 'us-east-1';
Expand Down
1 change: 0 additions & 1 deletion src/user/user.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand Down
6 changes: 3 additions & 3 deletions src/users/users.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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,
Expand Down
2 changes: 1 addition & 1 deletion src/utils/otp.util.ts
Original file line number Diff line number Diff line change
@@ -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);
Expand Down
Loading