feat: expand backend admin marketplace and scaling
فشلت بعض الفحوصات
/ deploy (push) Failing after 1m22s
فشلت بعض الفحوصات
/ deploy (push) Failing after 1m22s
هذا الالتزام موجود في:
@@ -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'
|
||||
}`,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
المرجع في مشكلة جديدة
حظر مستخدم