feat: expand backend admin marketplace and scaling
فشلت بعض الفحوصات
/ deploy (push) Failing after 1m22s

هذا الالتزام موجود في:
2026-05-14 16:17:12 +03:00
الأصل 0e76a4a9fc
التزام 5bd5e19a89
158 ملفات معدلة مع 19563 إضافات و3315 حذوفات

عرض الملف

@@ -1,6 +1,9 @@
import { BadRequestException, ForbiddenException, Injectable, NotFoundException } from '@nestjs/common';
import { BadRequestException, ForbiddenException, Injectable, Logger, NotFoundException } from '@nestjs/common';
import { Types } from 'mongoose';
import { decodeOffsetCursor, encodeOffsetCursor } from '../../common/utils/cursor.util';
import { buildPaginatedResponse } from '../../common/utils/pagination.util';
import { resolveMongoSortDirection } from '../../common/utils/sort.util';
import { NotificationsService } from '../notifications/notifications.service';
import { UsersRepository } from '../users/users.repository';
import { CreateConversationDto } from './dto/create-conversation.dto';
import { MessageQueryDto } from './dto/message-query.dto';
@@ -9,9 +12,12 @@ import { ChatRepository } from './chat.repository';
@Injectable()
export class ChatService {
private readonly logger = new Logger(ChatService.name);
constructor(
private readonly chatRepository: ChatRepository,
private readonly usersRepository: UsersRepository,
private readonly notificationsService: NotificationsService,
) {}
async createConversation(currentUserId: string, dto: CreateConversationDto) {
@@ -61,9 +67,13 @@ export class ChatService {
const limit = query.limit ?? 20;
const cursorOffset = decodeOffsetCursor(query.cursor);
const skip = cursorOffset ?? (page - 1) * limit;
const direction = resolveMongoSortDirection(query.sortOrder);
const [items, total] = await Promise.all([
this.chatRepository.findConversationsForUser(currentUserId, skip, limit),
this.chatRepository.findConversationsForUser(currentUserId, skip, limit, {
lastMessageAt: direction,
updatedAt: direction,
}),
this.chatRepository.countConversationsForUser(currentUserId),
]);
@@ -78,14 +88,15 @@ export class ChatService {
const nextOffset = skip + mappedItems.length;
const nextCursor = nextOffset < total ? encodeOffsetCursor(nextOffset) : null;
return {
items: mappedItems,
return buildPaginatedResponse(mappedItems, {
page,
limit,
total,
totalPages: Math.ceil(total / limit) || 1,
offset: skip,
currentCursor: query.cursor ?? null,
nextCursor,
};
mode: 'cursor',
});
}
async getMessages(currentUserId: string, conversationId: string, query: MessageQueryDto) {
@@ -94,9 +105,10 @@ export class ChatService {
const limit = query.limit ?? 20;
const cursorOffset = decodeOffsetCursor(query.cursor);
const skip = cursorOffset ?? (page - 1) * limit;
const sort = { createdAt: resolveMongoSortDirection(query.sortOrder) } as Record<string, 1 | -1>;
const [items, total] = await Promise.all([
this.chatRepository.findMessages(conversation.id, skip, limit),
this.chatRepository.findMessages(conversation.id, skip, limit, sort),
this.chatRepository.countMessages(conversation.id),
]);
@@ -104,14 +116,15 @@ export class ChatService {
const nextOffset = skip + items.length;
const nextCursor = nextOffset < total ? encodeOffsetCursor(nextOffset) : null;
return {
items,
return buildPaginatedResponse(items, {
page,
limit,
total,
totalPages: Math.ceil(total / limit) || 1,
offset: skip,
currentCursor: query.cursor ?? null,
nextCursor,
};
mode: 'cursor',
});
}
async sendMessage(currentUserId: string, dto: SendMessageDto) {
@@ -144,6 +157,12 @@ export class ChatService {
currentUserId,
preview,
);
await this.dispatchMessageNotifications(
currentUserId,
conversation.participantIds.map((id) => id.toString()),
conversation.id,
preview,
);
return message;
}
@@ -247,4 +266,32 @@ export class ChatService {
}
}
}
private async dispatchMessageNotifications(
actorId: string,
participantIds: string[],
conversationId: string,
previewText: string,
): Promise<void> {
for (const recipientId of participantIds) {
if (recipientId === actorId) {
continue;
}
try {
await this.notificationsService.createMessageNotification(
actorId,
recipientId,
conversationId,
previewText.slice(0, 160),
);
} catch (error) {
this.logger.warn(
`Message notification failed for actor=${actorId} recipient=${recipientId}: ${
error instanceof Error ? error.message : 'unknown error'
}`,
);
}
}
}
}