mirror of
https://github.com/outline/outline.git
synced 2026-06-13 11:25:03 +03:00
7e2d2542b0
* Initial plan * Split group notifications into separate background task - Created GroupMentionedInCommentNotificationsTask for handling group mention notifications - Updated CommentCreatedNotificationsTask to delegate group notifications - Updated CommentUpdatedNotificationsTask to delegate group notifications - Added comprehensive tests for the new task - Improved scalability with batch processing (10 members per batch) - Enhanced resilience by isolating group notification failures Co-authored-by: tommoor <380914+tommoor@users.noreply.github.com> * Optimize database queries in GroupMentionedInCommentNotificationsTask - Batch fetch users instead of individual queries for better performance - Add defensive comment explaining duplicate mentions check - Create user map for O(1) lookups within batch Co-authored-by: tommoor <380914+tommoor@users.noreply.github.com> * Add comment explaining concurrency control in batch processing Co-authored-by: tommoor <380914+tommoor@users.noreply.github.com> * Remove unnecessary document loading - use event properties directly - Removed Document import and document loading query - Use event.documentId and event.teamId directly - Reduces database queries and improves performance - canUserAccessDocument loads document internally when needed Co-authored-by: tommoor <380914+tommoor@users.noreply.github.com> * Fix TypeScript errors and test failures - Fixed test: buildComment requires documentId parameter - Fixed type error: use nullish coalescing for group.actorId ?? event.actorId - Fixed type definition: use Omit<CommentEvent, "data"> to avoid type conflicts - All TypeScript errors resolved Co-authored-by: tommoor <380914+tommoor@users.noreply.github.com> * test --------- Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: tommoor <380914+tommoor@users.noreply.github.com> Co-authored-by: Tom Moor <tom@getoutline.com>
85 lines
2.6 KiB
TypeScript
85 lines
2.6 KiB
TypeScript
import { Op } from "sequelize";
|
|
import { NotificationEventType } from "@shared/types";
|
|
import { Group, GroupUser, Notification, User } from "@server/models";
|
|
import type { CommentEvent } from "@server/types";
|
|
import { canUserAccessDocument } from "@server/utils/permissions";
|
|
import { BaseTask, TaskPriority } from "./base/BaseTask";
|
|
|
|
type GroupMentionEvent = Omit<CommentEvent, "data"> & {
|
|
data: {
|
|
groupId: string;
|
|
actorId: string;
|
|
};
|
|
};
|
|
|
|
export default class GroupMentionedInCommentNotificationsTask extends BaseTask<GroupMentionEvent> {
|
|
public async perform(event: GroupMentionEvent) {
|
|
const { groupId, actorId } = event.data;
|
|
|
|
// Defensive check: ensure group has mentions enabled.
|
|
// This is also checked in the parent task, but we verify here
|
|
// for resilience in case this task is scheduled directly.
|
|
const groupModel = await Group.findByPk(groupId);
|
|
if (groupModel?.disableMentions) {
|
|
return;
|
|
}
|
|
|
|
// Process group members in batches for scalability
|
|
await GroupUser.findAllInBatches<GroupUser>(
|
|
{
|
|
where: {
|
|
groupId,
|
|
userId: {
|
|
[Op.ne]: actorId,
|
|
},
|
|
},
|
|
order: [["permission", "ASC"]],
|
|
batchLimit: 10,
|
|
},
|
|
async (groupUsers) => {
|
|
// Batch fetch all users to reduce database queries
|
|
const userIds = groupUsers.map((gu) => gu.userId);
|
|
const users = await User.findAll({
|
|
where: {
|
|
id: userIds,
|
|
},
|
|
});
|
|
|
|
// Create a map for quick user lookup
|
|
const userMap = new Map(users.map((u) => [u.id, u]));
|
|
|
|
// Process notifications for this batch (limited to 10 concurrent operations
|
|
// by the batch size to avoid overwhelming the database connection pool)
|
|
await Promise.all(
|
|
groupUsers.map(async (groupUser) => {
|
|
const recipient = userMap.get(groupUser.userId);
|
|
if (
|
|
recipient &&
|
|
recipient.subscribedToEventType(
|
|
NotificationEventType.GroupMentionedInComment
|
|
) &&
|
|
(await canUserAccessDocument(recipient, event.documentId))
|
|
) {
|
|
await Notification.create({
|
|
event: NotificationEventType.GroupMentionedInComment,
|
|
groupId,
|
|
userId: recipient.id,
|
|
actorId,
|
|
teamId: event.teamId,
|
|
documentId: event.documentId,
|
|
commentId: event.modelId,
|
|
});
|
|
}
|
|
})
|
|
);
|
|
}
|
|
);
|
|
}
|
|
|
|
public get options() {
|
|
return {
|
|
priority: TaskPriority.Background,
|
|
};
|
|
}
|
|
}
|