This commit is contained in:
Oscar
2026-06-02 16:22:53 +03:00
parent dc44cdd639
commit bc3e48bcad
37 changed files with 973 additions and 1894 deletions

View File

@@ -19,13 +19,15 @@ export class ChatService {
) {}
async createChat(userId: string, dto: CreateChatDto) {
await this.assertProfileOwnership(userId, dto.profileId);
const [foundMatch] = await this.drizzleService.db
.select()
.from(match)
.where(
and(
eq(match.id, dto.matchId),
or(eq(match.user1Id, userId), eq(match.user2Id, userId)),
or(eq(match.profile1Id, dto.profileId), eq(match.profile2Id, dto.profileId)),
),
)
.limit(1);
@@ -37,50 +39,46 @@ export class ChatService {
.from(chat)
.where(
or(
and(
eq(chat.profile1Id, foundMatch.user1Id),
eq(chat.profile2Id, foundMatch.user2Id),
),
and(
eq(chat.profile1Id, foundMatch.user2Id),
eq(chat.profile2Id, foundMatch.user1Id),
),
and(eq(chat.profile1Id, foundMatch.profile1Id), eq(chat.profile2Id, foundMatch.profile2Id)),
and(eq(chat.profile1Id, foundMatch.profile2Id), eq(chat.profile2Id, foundMatch.profile1Id)),
),
)
.limit(1);
if (existingChat.length > 0) return existingChat[0];
const currentUser = await this.drizzleService.db
.select({ activeChatId: user.activeChatId })
.from(user)
.where(eq(user.id, userId))
const [currentProfile] = await this.drizzleService.db
.select({ activeChatId: profile.activeChatId })
.from(profile)
.where(eq(profile.id, dto.profileId))
.limit(1);
if (currentUser[0]?.activeChatId) {
if (currentProfile?.activeChatId) {
throw new BadRequestException(
'You already have an active chat. Close it before opening a new one.',
'Profile already has an active chat. Close it before opening a new one.',
);
}
const [newChat] = await this.drizzleService.db
.insert(chat)
.values({
profile1Id: foundMatch.user1Id,
profile2Id: foundMatch.user2Id,
profile1Id: foundMatch.profile1Id,
profile2Id: foundMatch.profile2Id,
status: 'active',
} as any)
.returning();
await this.drizzleService.db
.update(user)
.update(profile)
.set({ activeChatId: newChat.id } as any)
.where(or(eq(user.id, foundMatch.user1Id), eq(user.id, foundMatch.user2Id)));
.where(or(eq(profile.id, foundMatch.profile1Id), eq(profile.id, foundMatch.profile2Id)));
return newChat;
}
async closeChat(userId: string, chatId: string) {
async closeChat(userId: string, profileId: string, chatId: string) {
await this.assertProfileOwnership(userId, profileId);
const [foundChat] = await this.drizzleService.db
.select()
.from(chat)
@@ -88,7 +86,7 @@ export class ChatService {
.limit(1);
if (!foundChat) throw new NotFoundException('Chat not found');
if (foundChat.profile1Id !== userId && foundChat.profile2Id !== userId) {
if (foundChat.profile1Id !== profileId && foundChat.profile2Id !== profileId) {
throw new ForbiddenException('Not a chat participant');
}
@@ -98,26 +96,29 @@ export class ChatService {
.where(eq(chat.id, chatId));
await this.drizzleService.db
.update(user)
.update(profile)
.set({ activeChatId: null } as any)
.where(or(eq(user.id, foundChat.profile1Id), eq(user.id, foundChat.profile2Id)));
.where(or(eq(profile.id, foundChat.profile1Id), eq(profile.id, foundChat.profile2Id)));
return { message: 'Chat closed' };
}
async getMyChats(userId: string) {
async getChatsForProfile(userId: string, profileId: string) {
await this.assertProfileOwnership(userId, profileId);
return this.drizzleService.db
.select()
.from(chat)
.where(
and(
or(eq(chat.profile1Id, userId), eq(chat.profile2Id, userId)),
or(eq(chat.profile1Id, profileId), eq(chat.profile2Id, profileId)),
eq(chat.status, 'active'),
),
);
}
async getChatMessages(userId: string, chatId: string, page = 1, limit = 50) {
async getChatMessages(userId: string, profileId: string, chatId: string, page = 1, limit = 50) {
await this.assertProfileOwnership(userId, profileId);
const [foundChat] = await this.drizzleService.db
.select()
.from(chat)
@@ -125,7 +126,7 @@ export class ChatService {
.limit(1);
if (!foundChat) throw new NotFoundException('Chat not found');
if (foundChat.profile1Id !== userId && foundChat.profile2Id !== userId) {
if (foundChat.profile1Id !== profileId && foundChat.profile2Id !== profileId) {
throw new ForbiddenException('Not a chat participant');
}
@@ -139,7 +140,9 @@ export class ChatService {
.offset(offset);
}
async sendMessage(userId: string, chatId: string, dto: SendMessageDto) {
async sendMessage(userId: string, profileId: string, chatId: string, dto: SendMessageDto) {
await this.assertProfileOwnership(userId, profileId);
const [foundChat] = await this.drizzleService.db
.select()
.from(chat)
@@ -147,7 +150,7 @@ export class ChatService {
.limit(1);
if (!foundChat) throw new NotFoundException('Active chat not found');
if (foundChat.profile1Id !== userId && foundChat.profile2Id !== userId) {
if (foundChat.profile1Id !== profileId && foundChat.profile2Id !== profileId) {
throw new ForbiddenException('Not a chat participant');
}
@@ -159,31 +162,50 @@ export class ChatService {
.insert(message)
.values({
chatId,
userId,
profileId,
text: dto.text || null,
mediaUrl: dto.mediaUrl || null,
mediaType: dto.mediaType || null,
} as any)
.returning();
const recipientId =
foundChat.profile1Id === userId ? foundChat.profile2Id : foundChat.profile1Id;
const recipientProfileId =
foundChat.profile1Id === profileId ? foundChat.profile2Id : foundChat.profile1Id;
const [recipient] = await this.drizzleService.db
.select({ fcmToken: user.fcmToken })
.from(user)
.where(eq(user.id, recipientId))
const [recipientProfile] = await this.drizzleService.db
.select({ userId: profile.userId })
.from(profile)
.where(eq(profile.id, recipientProfileId))
.limit(1);
if (recipient?.fcmToken) {
await this.notificationsService.sendPushNotification(
recipient.fcmToken,
'New message',
dto.text?.substring(0, 100) || 'Media message',
{ chatId, messageId: newMessage.id, type: 'message' },
);
if (recipientProfile) {
const [recipientUser] = await this.drizzleService.db
.select({ fcmToken: user.fcmToken })
.from(user)
.where(eq(user.id, recipientProfile.userId))
.limit(1);
if (recipientUser?.fcmToken) {
await this.notificationsService.sendPushNotification(
recipientUser.fcmToken,
'New message',
dto.text?.substring(0, 100) || 'Media message',
{ chatId, messageId: newMessage.id, type: 'message' },
);
}
}
return newMessage;
}
private async assertProfileOwnership(userId: string, profileId: string) {
const [found] = await this.drizzleService.db
.select({ userId: profile.userId })
.from(profile)
.where(eq(profile.id, profileId))
.limit(1);
if (!found) throw new NotFoundException('Profile not found');
if (found.userId !== userId) throw new ForbiddenException('Profile does not belong to you');
}
}