Match Figma profile, blocks, auth, and archive backend
هذا الالتزام موجود في:
@@ -6,6 +6,7 @@ import { PostsModule } from '../posts/posts.module';
|
||||
import { UsersModule } from '../users/users.module';
|
||||
import { Like, LikeSchema } from '../likes/schemas/like.schema';
|
||||
import { FollowsModule } from '../follows/follows.module';
|
||||
import { BlocksModule } from '../blocks/blocks.module';
|
||||
import { Comment, CommentSchema } from './schemas/comment.schema';
|
||||
import { CommentsController } from './comments.controller';
|
||||
import { CommentsService } from './comments.service';
|
||||
@@ -22,6 +23,7 @@ import { CommentsRepository } from './comments.repository';
|
||||
NotificationsModule,
|
||||
UsersModule,
|
||||
FollowsModule,
|
||||
BlocksModule,
|
||||
],
|
||||
controllers: [CommentsController],
|
||||
providers: [CommentsService, CommentsRepository],
|
||||
|
||||
@@ -44,6 +44,9 @@ describe('CommentsService', () => {
|
||||
const followsRepository = {
|
||||
findOne: jest.fn(),
|
||||
};
|
||||
const blocksService = {
|
||||
hasBlockBetween: jest.fn().mockResolvedValue(false),
|
||||
};
|
||||
|
||||
const service = new CommentsService(
|
||||
commentsRepository as any,
|
||||
@@ -53,6 +56,7 @@ describe('CommentsService', () => {
|
||||
notificationsService as any,
|
||||
usersRepository as any,
|
||||
followsRepository as any,
|
||||
blocksService as any,
|
||||
);
|
||||
|
||||
const result = await service.update(userId, commentId, {
|
||||
@@ -82,4 +86,41 @@ describe('CommentsService', () => {
|
||||
}),
|
||||
);
|
||||
});
|
||||
|
||||
it('blocks comments when the commenter and post author have a block relation', async () => {
|
||||
const userId = new Types.ObjectId().toString();
|
||||
const authorId = new Types.ObjectId().toString();
|
||||
const postId = new Types.ObjectId().toString();
|
||||
const commentsRepository = {
|
||||
create: jest.fn(),
|
||||
};
|
||||
const postsRepository = {
|
||||
findById: jest.fn().mockResolvedValue({
|
||||
id: postId,
|
||||
authorId: new Types.ObjectId(authorId),
|
||||
commentsDisabled: false,
|
||||
commentsFollowersOnly: false,
|
||||
}),
|
||||
setCommentsCount: jest.fn(),
|
||||
};
|
||||
const blocksService = {
|
||||
hasBlockBetween: jest.fn().mockResolvedValue(true),
|
||||
};
|
||||
|
||||
const service = new CommentsService(
|
||||
commentsRepository as any,
|
||||
postsRepository as any,
|
||||
{ logSuperAdminAction: jest.fn() } as any,
|
||||
{ bumpGlobalVersion: jest.fn() } as any,
|
||||
{ createCommentNotification: jest.fn(), createMentionNotification: jest.fn() } as any,
|
||||
{ findByUsernames: jest.fn() } as any,
|
||||
{ findOne: jest.fn() } as any,
|
||||
blocksService as any,
|
||||
);
|
||||
|
||||
await expect(
|
||||
service.create(userId, { postId, content: 'Blocked comment' }),
|
||||
).rejects.toThrow('Post not found');
|
||||
expect(commentsRepository.create).not.toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -6,6 +6,7 @@ import { resolveMongoSortDirection } from '../../common/utils/sort.util';
|
||||
import { FeedVersionService } from '../../infrastructure/cache/feed-version.service';
|
||||
import { AuditService } from '../audit/audit.service';
|
||||
import { NotificationsService } from '../notifications/notifications.service';
|
||||
import { BlocksService } from '../blocks/blocks.service';
|
||||
import { FollowsRepository } from '../follows/follows.repository';
|
||||
import { PostsRepository } from '../posts/posts.repository';
|
||||
import { UsersRepository } from '../users/users.repository';
|
||||
@@ -48,6 +49,7 @@ export class CommentsService {
|
||||
private readonly notificationsService: NotificationsService,
|
||||
private readonly usersRepository: UsersRepository,
|
||||
private readonly followsRepository: FollowsRepository,
|
||||
private readonly blocksService: BlocksService,
|
||||
) {}
|
||||
|
||||
async create(userId: string, dto: CreateCommentDto) {
|
||||
@@ -63,6 +65,7 @@ export class CommentsService {
|
||||
if (!parent || parent.postId.toString() !== dto.postId) {
|
||||
throw new NotFoundException('Parent comment not found');
|
||||
}
|
||||
await this.assertNoBlockBetween(userId, parent.authorId.toString());
|
||||
parentRecipientId = parent.authorId.toString();
|
||||
}
|
||||
|
||||
@@ -391,15 +394,27 @@ export class CommentsService {
|
||||
|
||||
const authorId = this.extractEntityId(post.authorId);
|
||||
if (!post.commentsFollowersOnly || authorId === userId) {
|
||||
await this.assertNoBlockBetween(userId, authorId);
|
||||
return;
|
||||
}
|
||||
|
||||
await this.assertNoBlockBetween(userId, authorId);
|
||||
const followsAuthor = await this.followsRepository.findOne(userId, authorId);
|
||||
if (!followsAuthor) {
|
||||
throw new ForbiddenException('Only followers can comment on this post');
|
||||
}
|
||||
}
|
||||
|
||||
private async assertNoBlockBetween(actorId: string, targetUserId: string): Promise<void> {
|
||||
if (!targetUserId || actorId === targetUserId) {
|
||||
return;
|
||||
}
|
||||
const blocked = await this.blocksService.hasBlockBetween(actorId, targetUserId);
|
||||
if (blocked) {
|
||||
throw new NotFoundException('Post not found');
|
||||
}
|
||||
}
|
||||
|
||||
private matchesCommentFilter(content: string, keywords: string[] = []): boolean {
|
||||
const normalized = content.toLowerCase();
|
||||
return keywords
|
||||
@@ -546,6 +561,9 @@ export class CommentsService {
|
||||
}
|
||||
|
||||
for (const recipientId of recipients) {
|
||||
if (await this.blocksService.hasBlockBetween(actorId, recipientId)) {
|
||||
continue;
|
||||
}
|
||||
try {
|
||||
await this.notificationsService.createCommentNotification(actorId, recipientId, postId, {
|
||||
resourceType: 'post',
|
||||
@@ -632,6 +650,9 @@ export class CommentsService {
|
||||
if (excludedRecipientIds.has(mentionedUser.id)) {
|
||||
continue;
|
||||
}
|
||||
if (await this.blocksService.hasBlockBetween(actorId, mentionedUser.id)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
try {
|
||||
await this.notificationsService.createMentionNotification(actorId, mentionedUser.id, postId, {
|
||||
|
||||
المرجع في مشكلة جديدة
حظر مستخدم