Skip to content

Commit

Permalink
do less round trips to db
Browse files Browse the repository at this point in the history
  • Loading branch information
sampaiodiego committed Dec 5, 2023
1 parent 05f45dc commit b44d238
Show file tree
Hide file tree
Showing 3 changed files with 114 additions and 89 deletions.
103 changes: 65 additions & 38 deletions apps/meteor/server/services/messages/hooks/BeforeSaveJumpToMessage.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,8 +33,8 @@ const validateAttachmentDeepness = (message: IMessage, quoteChainLimit: number):
};

type JumpToMessageInit = {
getMessage(messageId: IMessage['_id']): Promise<IMessage | null>;
getRoom(roomId: IRoom['_id']): Promise<IRoom | IOmnichannelRoom | null>;
getMessages(messageIds: IMessage['_id'][]): Promise<IMessage[]>;
getRooms(roomIds: IRoom['_id'][]): Promise<IRoom[] | IOmnichannelRoom[] | null>;
canAccessRoom(room: IRoom, user: Pick<IUser, '_id' | 'username' | 'name' | 'language'>): Promise<boolean>;
getUserAvatarURL(user?: string): string;
};
Expand All @@ -43,17 +43,17 @@ type JumpToMessageInit = {
* Transform URLs in messages into quote attachments
*/
export class BeforeSaveJumpToMessage {
private getMessage: JumpToMessageInit['getMessage'];
private getMessages: JumpToMessageInit['getMessages'];

private getRoom: JumpToMessageInit['getRoom'];
private getRooms: JumpToMessageInit['getRooms'];

private canAccessRoom: JumpToMessageInit['canAccessRoom'];

private getUserAvatarURL: JumpToMessageInit['getUserAvatarURL'];

constructor(options: JumpToMessageInit) {
this.getMessage = options.getMessage;
this.getRoom = options.getRoom;
this.getMessages = options.getMessages;
this.getRooms = options.getRooms;
this.canAccessRoom = options.canAccessRoom;
this.getUserAvatarURL = options.getUserAvatarURL;
}
Expand All @@ -76,58 +76,85 @@ export class BeforeSaveJumpToMessage {
return message;
}

for await (const item of message.urls) {
// if the URL doesn't belong to the current server, skip
if (!item.url.includes(config.siteUrl)) {
continue;
}
const linkedMessages = message.urls
.filter((item) => item.url.includes(config.siteUrl))
.map((item) => {
const urlObj = URL.parse(item.url);

const urlObj = URL.parse(item.url);
// if the URL doesn't have query params (doesn't reference message) skip
if (!urlObj.query) {
return;
}

// if the URL doesn't have query params (doesn't reference message) skip
if (!urlObj.query) {
continue;
}
const { msg: msgId } = QueryString.parse(urlObj.query);

if (typeof msgId !== 'string') {
return;
}

return { msgId, url: item.url };
})
.filter(Boolean);

const msgs = await this.getMessages(linkedMessages.map((linkedMsg) => linkedMsg?.msgId) as string[]);

const validMessages = msgs.filter((msg) => validateAttachmentDeepness(msg, config.chainLimit));

const rooms = await this.getRooms(validMessages.map((msg) => msg.rid));

const roomsWithPermission =
rooms &&
(await Promise.all(
rooms.map(async (room) => {
if (!!message.token && isOmnichannelRoom(room) && !!room.v?.token && message.token === room.v.token) {
return room;
}

const { msg: msgId } = QueryString.parse(urlObj.query);
if (currentUser && (await this.canAccessRoom(room, currentUser))) {
return room;
}
}),
));

if (typeof msgId !== 'string') {
const validRooms = roomsWithPermission?.filter((room) => !!room);

const { useRealName } = config;

const quotes = [];

for (const item of message.urls) {
if (!item.url.includes(config.siteUrl)) {
continue;
}

const messageFromUrl = await this.getMessage(msgId);

const jumpToMessage = messageFromUrl && validateAttachmentDeepness(messageFromUrl, config.chainLimit);
if (!jumpToMessage) {
const linkedMessage = linkedMessages.find((msg) => msg?.url === item.url);
if (!linkedMessage) {
continue;
}

// validates if user can see the message
// user has to belong to the room the message was first wrote in
const room = await this.getRoom(jumpToMessage.rid);
if (!room) {
const messageFromUrl = validMessages.find((msg) => msg._id === linkedMessage.msgId);
if (!messageFromUrl) {
continue;
}

const isLiveChatRoomVisitor = !!message.token && isOmnichannelRoom(room) && !!room.v?.token && message.token === room.v.token;
const canAccessRoomForUser = isLiveChatRoomVisitor || (currentUser && (await this.canAccessRoom(room, currentUser)));
if (!canAccessRoomForUser) {
if (!validRooms?.find((room) => room?._id === messageFromUrl.rid)) {
continue;
}

message.attachments = message.attachments || [];
item.ignoreParse = true;

// Only QuoteAttachments have "message_link" property
const index = message.attachments.findIndex((a) => isQuoteAttachment(a) && a.message_link === item.url);
if (index > -1) {
message.attachments.splice(index, 1);
const index = message.attachments?.findIndex((a) => isQuoteAttachment(a) && a.message_link === item.url);
if (index !== undefined && index > -1) {
message.attachments?.splice(index, 1);
}

const { useRealName } = config;
quotes.push(createQuoteAttachment(messageFromUrl, item.url, useRealName, this.getUserAvatarURL(messageFromUrl.u.username)));
}

message.attachments.push(
createQuoteAttachment(jumpToMessage, item.url, useRealName, this.getUserAvatarURL(jumpToMessage.u.username)),
);
item.ignoreParse = true;
if (quotes.length > 0) {
message.attachments = message.attachments || [];
message.attachments.push(...quotes);
}

return message;
Expand Down
9 changes: 4 additions & 5 deletions apps/meteor/server/services/messages/service.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import type { IMessageService } from '@rocket.chat/core-services';
import { Authorization, ServiceClassInternal } from '@rocket.chat/core-services';
import type { IOmnichannelRoom } from '@rocket.chat/core-typings';
import { type IMessage, type MessageTypesValues, type IUser, type IRoom, isEditedMessage } from '@rocket.chat/core-typings';
import { Messages, Rooms } from '@rocket.chat/models';

Expand Down Expand Up @@ -33,11 +32,11 @@ export class MessageService extends ServiceClassInternal implements IMessageServ
this.badWords = new BeforeSaveBadWords();
this.spotify = new BeforeSaveSpotify();
this.jumpToMessage = new BeforeSaveJumpToMessage({
getMessage(messageId: IMessage['_id']): Promise<IMessage | null> {
return Messages.findOneById(messageId);
getMessages(messageIds) {
return Messages.findVisibleByIds(messageIds).toArray();
},
getRoom(roomId: IRoom['_id']): Promise<IOmnichannelRoom | null> {
return Rooms.findOneById<IOmnichannelRoom>(roomId);
getRooms(roomIds) {
return Rooms.findByIds(roomIds).toArray();
},
canAccessRoom(room: IRoom, user: IUser): Promise<boolean> {
return Authorization.canAccessRoom(room, user);
Expand Down
Loading

0 comments on commit b44d238

Please sign in to comment.