first commit

This commit is contained in:
Oscar
2026-06-02 15:52:22 +03:00
commit dc44cdd639
105 changed files with 14674 additions and 0 deletions

View File

@@ -0,0 +1,189 @@
import {
BadRequestException,
ForbiddenException,
Injectable,
NotFoundException,
} from '@nestjs/common';
import { and, eq, or } from 'drizzle-orm';
import { DrizzleService } from '../../database/drizzle.service';
import { chat, match, message, profile, user } from '../../database/schema';
import { NotificationsService } from '../../notifications/notifications.service';
import { CreateChatDto } from './dto/create-chat.dto';
import { SendMessageDto } from './dto/send-message.dto';
@Injectable()
export class ChatService {
constructor(
private readonly drizzleService: DrizzleService,
private readonly notificationsService: NotificationsService,
) {}
async createChat(userId: string, dto: CreateChatDto) {
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)),
),
)
.limit(1);
if (!foundMatch) throw new NotFoundException('Match not found');
const existingChat = await this.drizzleService.db
.select()
.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),
),
),
)
.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))
.limit(1);
if (currentUser[0]?.activeChatId) {
throw new BadRequestException(
'You already have 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,
status: 'active',
} as any)
.returning();
await this.drizzleService.db
.update(user)
.set({ activeChatId: newChat.id } as any)
.where(or(eq(user.id, foundMatch.user1Id), eq(user.id, foundMatch.user2Id)));
return newChat;
}
async closeChat(userId: string, chatId: string) {
const [foundChat] = await this.drizzleService.db
.select()
.from(chat)
.where(eq(chat.id, chatId))
.limit(1);
if (!foundChat) throw new NotFoundException('Chat not found');
if (foundChat.profile1Id !== userId && foundChat.profile2Id !== userId) {
throw new ForbiddenException('Not a chat participant');
}
await this.drizzleService.db
.update(chat)
.set({ status: 'closed' } as any)
.where(eq(chat.id, chatId));
await this.drizzleService.db
.update(user)
.set({ activeChatId: null } as any)
.where(or(eq(user.id, foundChat.profile1Id), eq(user.id, foundChat.profile2Id)));
return { message: 'Chat closed' };
}
async getMyChats(userId: string) {
return this.drizzleService.db
.select()
.from(chat)
.where(
and(
or(eq(chat.profile1Id, userId), eq(chat.profile2Id, userId)),
eq(chat.status, 'active'),
),
);
}
async getChatMessages(userId: string, chatId: string, page = 1, limit = 50) {
const [foundChat] = await this.drizzleService.db
.select()
.from(chat)
.where(eq(chat.id, chatId))
.limit(1);
if (!foundChat) throw new NotFoundException('Chat not found');
if (foundChat.profile1Id !== userId && foundChat.profile2Id !== userId) {
throw new ForbiddenException('Not a chat participant');
}
const offset = (page - 1) * limit;
return this.drizzleService.db
.select()
.from(message)
.where(eq(message.chatId, chatId))
.orderBy(message.createdAt)
.limit(limit)
.offset(offset);
}
async sendMessage(userId: string, chatId: string, dto: SendMessageDto) {
const [foundChat] = await this.drizzleService.db
.select()
.from(chat)
.where(and(eq(chat.id, chatId), eq(chat.status, 'active')))
.limit(1);
if (!foundChat) throw new NotFoundException('Active chat not found');
if (foundChat.profile1Id !== userId && foundChat.profile2Id !== userId) {
throw new ForbiddenException('Not a chat participant');
}
if (!dto.text && !dto.mediaUrl) {
throw new BadRequestException('Message must have text or media');
}
const [newMessage] = await this.drizzleService.db
.insert(message)
.values({
chatId,
userId,
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 [recipient] = await this.drizzleService.db
.select({ fcmToken: user.fcmToken })
.from(user)
.where(eq(user.id, recipientId))
.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' },
);
}
return newMessage;
}
}