refactor: Rooms model 1/2 (#28694)

pull/28675/head^2
Kevin Aleman 3 years ago committed by GitHub
parent 151323a447
commit 35499f5b89
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 14
      apps/meteor/app/apps/server/bridges/rooms.ts
  2. 2
      apps/meteor/app/channel-settings/server/functions/saveRoomName.js
  3. 55
      apps/meteor/app/importer/server/classes/ImportDataConverter.ts
  4. 8
      apps/meteor/app/irc/server/irc-bridge/peerHandlers/sentMessage.js
  5. 9
      apps/meteor/app/lib/server/functions/addUserToDefaultChannels.ts
  6. 17
      apps/meteor/app/lib/server/functions/createDirectRoom.ts
  7. 2
      apps/meteor/app/lib/server/functions/createRoom.ts
  8. 10
      apps/meteor/app/lib/server/functions/insertMessage.js
  9. 14
      apps/meteor/app/lib/server/lib/notifyUsersOnMessage.js
  10. 6
      apps/meteor/app/livechat/server/lib/RoutingManager.js
  11. 4
      apps/meteor/app/models/server/models/Messages.js
  12. 169
      apps/meteor/app/models/server/models/Rooms.js
  13. 4
      apps/meteor/app/slackbridge/server/RocketAdapter.js
  14. 26
      apps/meteor/app/slackbridge/server/SlackAdapter.js
  15. 11
      apps/meteor/app/utils/server/lib/getValidRoomName.js
  16. 16
      apps/meteor/ee/server/lib/audit/methods.ts
  17. 7
      apps/meteor/ee/server/lib/ldap/Manager.ts
  18. 16
      apps/meteor/ee/server/lib/oauth/Manager.ts
  19. 7
      apps/meteor/server/methods/createDirectMessage.ts
  20. 28
      apps/meteor/server/models/raw/Rooms.js
  21. 2
      apps/meteor/server/publications/room/index.ts
  22. 3
      packages/model-typings/src/models/IRoomsModel.ts

@ -5,10 +5,10 @@ import type { IUser } from '@rocket.chat/apps-engine/definition/users';
import type { IMessage } from '@rocket.chat/apps-engine/definition/messages';
import { Meteor } from 'meteor/meteor';
import type { ISubscription, IUser as ICoreUser } from '@rocket.chat/core-typings';
import { Subscriptions } from '@rocket.chat/models';
import { Subscriptions, Rooms } from '@rocket.chat/models';
import type { AppServerOrchestrator } from '../../../../ee/server/apps/orchestrator';
import { Rooms, Users } from '../../../models/server';
import { Users } from '../../../models/server';
import { addUserToRoom } from '../../../lib/server/functions/addUserToRoom';
import { deleteRoom } from '../../../lib/server/functions/deleteRoom';
@ -72,7 +72,7 @@ export class AppRoomBridge extends RoomBridge {
protected async getCreatorById(roomId: string, appId: string): Promise<IUser | undefined> {
this.orch.debugLog(`The App ${appId} is getting the room's creator by id: "${roomId}"`);
const room = Rooms.findOneById(roomId);
const room = await Rooms.findOneById(roomId);
if (!room || !room.u || !room.u._id) {
return undefined;
@ -84,7 +84,7 @@ export class AppRoomBridge extends RoomBridge {
protected async getCreatorByName(roomName: string, appId: string): Promise<IUser | undefined> {
this.orch.debugLog(`The App ${appId} is getting the room's creator by name: "${roomName}"`);
const room = Rooms.findOneByName(roomName, {});
const room = await Rooms.findOneByName(roomName, {});
if (!room || !room.u || !room.u._id) {
return undefined;
@ -111,13 +111,13 @@ export class AppRoomBridge extends RoomBridge {
protected async update(room: IRoom, members: Array<string> = [], appId: string): Promise<void> {
this.orch.debugLog(`The App ${appId} is updating a room.`);
if (!room.id || !Rooms.findOneById(room.id)) {
if (!room.id || !(await Rooms.findOneById(room.id))) {
throw new Error('A room must exist to update.');
}
const rm = await this.orch.getConverters()?.get('rooms').convertAppRoom(room);
Rooms.update(rm._id, rm);
await Rooms.updateOne({ _id: rm._id }, { $set: rm });
for await (const username of members) {
const member = Users.findOneByUsername(username, {});
@ -151,7 +151,7 @@ export class AppRoomBridge extends RoomBridge {
rcMessage = this.orch.getConverters()?.get('messages').convertAppMessage(parentMessage);
}
if (!rcRoom.prid || !Rooms.findOneById(rcRoom.prid)) {
if (!rcRoom.prid || !(await Rooms.findOneById(rcRoom.prid))) {
throw new Error('There must be a parent room to create a discussion.');
}

@ -13,7 +13,7 @@ const updateFName = async (rid, displayName) => {
};
const updateRoomName = async (rid, displayName) => {
const slugifiedRoomName = getValidRoomName(displayName, rid);
const slugifiedRoomName = await getValidRoomName(displayName, rid);
// Check if the username is available
if (!(await checkUsernameAvailability(slugifiedRoomName))) {

@ -21,6 +21,7 @@ import { Users, Rooms, Subscriptions } from '../../../models/server';
import { generateUsernameSuggestion, insertMessage, saveUserIdentity, addUserToDefaultChannels } from '../../../lib/server';
import { setUserActiveStatus } from '../../../lib/server/functions/setUserActiveStatus';
import type { Logger } from '../../../../server/lib/logger/Logger';
import { getValidRoomName } from '../../../utils/server/lib/getValidRoomName';
type IRoom = Record<string, any>;
type IMessage = Record<string, any>;
@ -494,10 +495,10 @@ export class ImportDataConverter {
return result;
}
getMentionedChannelData(importId: string): IMentionedChannel | undefined {
async getMentionedChannelData(importId: string): Promise<IMentionedChannel | undefined> {
// loading the name will also store the id on the cache if it's missing, so this won't run two queries
const name = this.findImportedRoomName(importId);
const _id = this.findImportedRoomId(importId);
const name = await this.findImportedRoomName(importId);
const _id = await this.findImportedRoomId(importId);
if (name && _id) {
return {
@ -507,8 +508,9 @@ export class ImportDataConverter {
}
// If the importId was not found, check if we have a room with that name
const room = Rooms.findOneByNonValidatedName(importId, { fields: { name: 1 } });
if (room) {
const roomName = await getValidRoomName(importId.trim(), undefined, { allowDuplicates: true });
const room = await RoomsRaw.findOneByNonValidatedName(roomName, { projection: { name: 1 } });
if (room?.name) {
this.addRoomToCache(importId, room._id);
this.addRoomNameToCache(importId, room.name);
@ -519,15 +521,15 @@ export class ImportDataConverter {
}
}
convertMessageChannels(message: IImportMessage): Array<IMentionedChannel> | undefined {
async convertMessageChannels(message: IImportMessage): Promise<IMentionedChannel[] | undefined> {
const { channels } = message;
if (!channels) {
return;
}
const result: Array<IMentionedChannel> = [];
for (const importId of channels) {
const { name, _id } = this.getMentionedChannelData(importId) || {};
for await (const importId of channels) {
const { name, _id } = (await this.getMentionedChannelData(importId)) || {};
if (!_id || !name) {
this._logger.warn(`Mentioned room not found: ${importId}`);
@ -570,7 +572,7 @@ export class ImportDataConverter {
throw new Error('importer-message-unknown-user');
}
const rid = this.findImportedRoomId(data.rid);
const rid = await this.findImportedRoomId(data.rid);
if (!rid) {
throw new Error('importer-message-unknown-room');
}
@ -580,7 +582,7 @@ export class ImportDataConverter {
// Convert the mentions and channels first because these conversions can also modify the msg in the message object
const mentions = data.mentions && this.convertMessageMentions(data);
const channels = data.channels && this.convertMessageChannels(data);
const channels = data.channels && (await this.convertMessageChannels(data));
const msgObj: IMessage = {
rid,
@ -617,7 +619,7 @@ export class ImportDataConverter {
}
try {
insertMessage(creator, msgObj, rid, true);
await insertMessage(creator, msgObj, rid, true);
} catch (e) {
this._logger.warn(`Failed to import message with timestamp ${String(msgObj.ts)} to room ${rid}`);
this._logger.error(e);
@ -661,18 +663,18 @@ export class ImportDataConverter {
}
}
findImportedRoomId(importId: string): string | null {
async findImportedRoomId(importId: string): Promise<string | null> {
if (this._roomCache.has(importId)) {
return this._roomCache.get(importId) as string;
}
const options = {
fields: {
projection: {
_id: 1,
},
};
const room = Rooms.findOneByImportId(importId, options);
const room = await RoomsRaw.findOneByImportId(importId, options);
if (room) {
return this.addRoomToCache(importId, room._id);
}
@ -680,24 +682,26 @@ export class ImportDataConverter {
return null;
}
findImportedRoomName(importId: string): string | undefined {
async findImportedRoomName(importId: string): Promise<string | undefined> {
if (this._roomNameCache.has(importId)) {
return this._roomNameCache.get(importId) as string;
}
const options = {
fields: {
projection: {
_id: 1,
name: 1,
},
};
const room = Rooms.findOneByImportId(importId, options);
const room = await RoomsRaw.findOneByImportId(importId, options);
if (room) {
if (!this._roomCache.has(importId)) {
this.addRoomToCache(importId, room._id);
}
return this.addRoomNameToCache(importId, room.name);
if (room?.name) {
return this.addRoomNameToCache(importId, room.name);
}
}
}
@ -874,9 +878,9 @@ export class ImportDataConverter {
.filter((user) => user);
}
findExistingRoom(data: IImportChannel): IRoom {
async findExistingRoom(data: IImportChannel): Promise<IRoom | null> {
if (data._id && data._id.toUpperCase() === 'GENERAL') {
const room = Rooms.findOneById('GENERAL', {});
const room = await RoomsRaw.findOneById('GENERAL', {});
// Prevent the importer from trying to create a new general
if (!room) {
throw new Error('importer-channel-general-not-found');
@ -891,10 +895,15 @@ export class ImportDataConverter {
throw new Error('importer-channel-missing-users');
}
return Rooms.findDirectRoomContainingAllUsernames(users, {});
return RoomsRaw.findDirectRoomContainingAllUsernames(users, {});
}
if (!data.name) {
return null;
}
return Rooms.findOneByNonValidatedName(data.name, {});
const roomName = await getValidRoomName(data.name.trim(), undefined, { allowDuplicates: true });
return RoomsRaw.findOneByNonValidatedName(roomName, {});
}
protected async getChannelsToImport(): Promise<Array<IImportChannelRecord>> {
@ -921,7 +930,7 @@ export class ImportDataConverter {
throw new Error('importer-channel-missing-import-id');
}
const existingRoom = this.findExistingRoom(data);
const existingRoom = await this.findExistingRoom(data);
if (existingRoom) {
this.updateRoom(existingRoom, data, startedByUserId);

@ -1,4 +1,6 @@
import { Users, Rooms } from '../../../../models/server';
import { Rooms } from '@rocket.chat/models';
import { Users } from '../../../../models/server';
import { sendMessage, createDirectRoom } from '../../../../lib/server';
/*
*
@ -10,7 +12,7 @@ const getDirectRoom = async (source, target) => {
const uids = [source._id, target._id];
const { _id, ...extraData } = await createDirectRoom([source, target]);
const room = Rooms.findOneDirectRoomContainingAllUserIDs(uids);
const room = await Rooms.findOneDirectRoomContainingAllUserIDs(uids);
if (room) {
return {
t: 'd',
@ -37,7 +39,7 @@ export default async function handleSentMessage(args) {
let room;
if (args.roomName) {
room = Rooms.findOneByName(args.roomName);
room = await Rooms.findOneByName(args.roomName);
} else {
const recipientUser = Users.findOne({
'profile.irc.nick': args.recipientNick,

@ -1,15 +1,14 @@
import type { IUser } from '@rocket.chat/core-typings';
import { Subscriptions } from '@rocket.chat/models';
import { Subscriptions, Rooms } from '@rocket.chat/models';
import { Message } from '@rocket.chat/core-services';
import { Rooms } from '../../../models/server';
import { callbacks } from '../../../../lib/callbacks';
export const addUserToDefaultChannels = async function (user: IUser, silenced?: boolean): Promise<void> {
callbacks.run('beforeJoinDefaultChannels', user);
const defaultRooms = Rooms.findByDefaultAndTypes(true, ['c', 'p'], {
fields: { usernames: 0 },
}).fetch();
const defaultRooms = await Rooms.findByDefaultAndTypes(true, ['c', 'p'], {
projection: { usernames: 0 },
}).toArray();
for await (const room of defaultRooms) {
if (!(await Subscriptions.findOneByRoomIdAndUserId(room._id, user._id, { projection: { _id: 1 } }))) {
// Add a subscription to this user

@ -2,11 +2,11 @@ import { AppsEngineException } from '@rocket.chat/apps-engine/definition/excepti
import { Meteor } from 'meteor/meteor';
import { Random } from '@rocket.chat/random';
import type { ICreatedRoom, ISubscription, IUser } from '@rocket.chat/core-typings';
import { Subscriptions } from '@rocket.chat/models';
import { Subscriptions, Rooms } from '@rocket.chat/models';
import type { MatchKeysAndValues } from 'mongodb';
import type { ISubscriptionExtraData } from '@rocket.chat/core-services';
import { Users, Rooms } from '../../../models/server';
import { Users } from '../../../models/server';
import { Apps } from '../../../../ee/server/apps';
import { callbacks } from '../../../../lib/callbacks';
import { settings } from '../../../settings/server';
@ -71,8 +71,8 @@ export async function createDirectRoom(
// Deprecated: using users' _id to compose the room _id is deprecated
const room =
uids.length === 2
? Rooms.findOneById(uids.join(''), { fields: { _id: 1 } })
: Rooms.findOneDirectRoomContainingAllUserIDs(uids, { fields: { _id: 1 } });
? await Rooms.findOneById(uids.join(''), { projection: { _id: 1 } })
: await Rooms.findOneDirectRoomContainingAllUserIDs(uids, { projection: { _id: 1 } });
const isNewRoom = !room;
@ -114,7 +114,8 @@ export async function createDirectRoom(
delete tmpRoom._USERNAMES;
}
const rid = room?._id || Rooms.insert(roomInfo);
// @ts-expect-error - TODO: room expects `u` to be passed, but it's not part of the original object in here
const rid = room?._id || (await Rooms.insertOne(roomInfo)).insertedId;
if (roomMembers.length === 1) {
// dm to yourself
@ -154,7 +155,7 @@ export async function createDirectRoom(
// If the room is new, run a callback
if (isNewRoom) {
const insertedRoom = Rooms.findOneById(rid);
const insertedRoom = await Rooms.findOneById(rid);
callbacks.run('afterCreateDirectRoom', insertedRoom, { members: roomMembers, creatorId: options?.creator });
@ -162,10 +163,12 @@ export async function createDirectRoom(
}
return {
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
...room!,
_id: String(rid),
usernames,
t: 'd',
rid,
inserted: isNewRoom,
...room,
};
}

@ -72,7 +72,7 @@ export const createRoom = async <T extends RoomType>(
fname: name,
_updatedAt: now,
...extraData,
name: getValidRoomName(name.trim(), undefined, {
name: await getValidRoomName(name.trim(), undefined, {
...(options?.nameValidationRegex && { nameValidationRegex: options.nameValidationRegex }),
}),
t: type,

@ -1,8 +1,10 @@
import { Messages, Rooms } from '../../../models/server';
import { Rooms } from '@rocket.chat/models';
import { Messages } from '../../../models/server';
import { validateMessage, prepareMessageObject } from './sendMessage';
import { parseUrlsInMessage } from './parseUrlsInMessage';
export const insertMessage = function (user, message, rid, upsert = false) {
export const insertMessage = async function (user, message, rid, upsert = false) {
if (!user || !message || !rid) {
return false;
}
@ -23,12 +25,12 @@ export const insertMessage = function (user, message, rid, upsert = false) {
message,
);
if (!existingMessage) {
Rooms.incMsgCountById(rid, 1);
await Rooms.incMsgCountById(rid, 1);
}
message._id = _id;
} else {
message._id = Messages.insert(message);
Rooms.incMsgCountById(rid, 1);
await Rooms.incMsgCountById(rid, 1);
}
return message;

@ -1,8 +1,8 @@
import moment from 'moment';
import { escapeRegExp } from '@rocket.chat/string-helpers';
import { Subscriptions as SubscriptionsRaw, Rooms as RoomsRaw } from '@rocket.chat/models';
import { Subscriptions as SubscriptionsRaw, Rooms } from '@rocket.chat/models';
import { Rooms, Subscriptions } from '../../../models/server';
import { Subscriptions } from '../../../models/server';
import { settings } from '../../../settings/server';
import { callbacks } from '../../../../lib/callbacks';
@ -157,7 +157,7 @@ export async function notifyUsersOnMessage(message, room) {
if (message.editedAt) {
if (Math.abs(moment(message.editedAt).diff()) > 60000) {
// TODO: Review as I am not sure how else to get around this as the incrementing of the msgs count shouldn't be in this callback
Rooms.incMsgCountById(message.rid, 1);
await Rooms.incMsgCountById(message.rid, 1);
return message;
}
@ -167,25 +167,25 @@ export async function notifyUsersOnMessage(message, room) {
(!message.tmid || message.tshow) &&
(!room.lastMessage || room.lastMessage._id === message._id)
) {
await RoomsRaw.setLastMessageById(message.rid, message);
await Rooms.setLastMessageById(message.rid, message);
}
return message;
}
if (message.ts && Math.abs(moment(message.ts).diff()) > 60000) {
Rooms.incMsgCountById(message.rid, 1);
await Rooms.incMsgCountById(message.rid, 1);
return message;
}
// if message sent ONLY on a thread, skips the rest as it is done on a callback specific to threads
if (message.tmid && !message.tshow) {
Rooms.incMsgCountById(message.rid, 1);
await Rooms.incMsgCountById(message.rid, 1);
return message;
}
// Update all the room activity tracker fields
await RoomsRaw.incMsgCountAndSetLastMessageById(message.rid, 1, message.ts, settings.get('Store_Last_Message') && message);
await Rooms.incMsgCountAndSetLastMessageById(message.rid, 1, message.ts, settings.get('Store_Last_Message') && message);
await updateUsersSubscriptions(message, room);

@ -1,6 +1,6 @@
import { Meteor } from 'meteor/meteor';
import { Match, check } from 'meteor/check';
import { LivechatInquiry, LivechatRooms, Subscriptions } from '@rocket.chat/models';
import { LivechatInquiry, LivechatRooms, Subscriptions, Rooms } from '@rocket.chat/models';
import { Message } from '@rocket.chat/core-services';
import {
@ -15,7 +15,7 @@ import {
} from './Helper';
import { callbacks } from '../../../../lib/callbacks';
import { Logger } from '../../../../server/lib/logger/Logger';
import { Rooms, Users } from '../../../models/server';
import { Users } from '../../../models/server';
import { Apps, AppEvents } from '../../../../ee/server/apps';
const logger = new Logger('RoutingManager');
@ -102,7 +102,7 @@ export const RoutingManager = {
}
await LivechatRooms.changeAgentByRoomId(rid, agent);
Rooms.incUsersCountById(rid);
await Rooms.incUsersCountById(rid);
const user = Users.findOneById(agent.agentId);
const room = await LivechatRooms.findOneById(rid);

@ -1,7 +1,7 @@
import _ from 'underscore';
import { Rooms } from '@rocket.chat/models';
import { Base } from './_Base';
import Rooms from './Rooms';
import { settings } from '../../../settings/server';
export class Messages extends Base {
@ -87,7 +87,7 @@ export class Messages extends Base {
_.extend(record, extraData);
record._id = this.insertOrUpsert(record);
Rooms.incMsgCountById(roomId, 1);
Promise.await(Rooms.incMsgCountById(roomId, 1));
return record;
}

@ -1,186 +1,17 @@
import { Base } from './_Base';
import { trim } from '../../../../lib/utils/stringUtils';
class Rooms extends Base {
constructor(...args) {
super(...args);
this.tryEnsureIndex({ name: 1 }, { unique: true, sparse: true });
this.tryEnsureIndex({ default: 1 }, { sparse: true });
this.tryEnsureIndex({ featured: 1 }, { sparse: true });
this.tryEnsureIndex({ muted: 1 }, { sparse: true });
this.tryEnsureIndex({ t: 1 });
this.tryEnsureIndex({ 'u._id': 1 });
this.tryEnsureIndex({ ts: 1 });
// discussions
this.tryEnsureIndex({ prid: 1 }, { sparse: true });
this.tryEnsureIndex({ fname: 1 }, { sparse: true });
// field used for DMs only
this.tryEnsureIndex({ uids: 1 }, { sparse: true });
this.tryEnsureIndex({ createdOTR: 1 }, { sparse: true });
this.tryEnsureIndex({ encrypted: 1 }, { sparse: true }); // used on statistics
this.tryEnsureIndex({ broadcast: 1 }, { sparse: true }); // used on statistics
this.tryEnsureIndex({ 'streamingOptions.type': 1 }, { sparse: true }); // used on statistics
this.tryEnsureIndex(
{
teamId: 1,
teamDefault: 1,
},
{ sparse: true },
);
}
findOneByImportId(_id, options) {
const query = { importIds: _id };
return this.findOne(query, options);
}
findOneByNonValidatedName(name, options) {
const room = this.findOneByNameOrFname(name, options);
if (room) {
return room;
}
let channelName = trim(name);
try {
// TODO evaluate if this function call should be here
const { getValidRoomName } = Promise.await(import('../../../utils/server/lib/getValidRoomName'));
channelName = getValidRoomName(channelName, null, { allowDuplicates: true });
} catch (e) {
console.error(e);
}
return this.findOneByName(channelName, options);
}
findOneByName(name, options) {
const query = { name };
return this.findOne(query, options);
}
findOneByNameOrFname(name, options) {
const query = {
$or: [
{
name,
},
{
fname: name,
},
],
};
return this.findOne(query, options);
}
findOneByNameAndNotId(name, rid) {
const query = {
_id: { $ne: rid },
name,
};
return this.findOne(query);
}
findOneByDisplayName(fname, options) {
const query = { fname };
return this.findOne(query, options);
}
// FIND
findByDefaultAndTypes(defaultValue, types, options) {
const query = {
default: defaultValue,
t: {
$in: types,
},
};
return this.find(query, options);
}
findDirectRoomContainingAllUsernames(usernames, options) {
const query = {
t: 'd',
usernames: { $size: usernames.length, $all: usernames },
usersCount: usernames.length,
};
return this.findOne(query, options);
}
findOneDirectRoomContainingAllUserIDs(uid, options) {
const query = {
t: 'd',
uids: { $size: uid.length, $all: uid },
};
return this.findOne(query, options);
}
// UPDATE
incMsgCountById(_id, inc = 1) {
const query = { _id };
const update = {
$inc: {
msgs: inc,
},
};
return this.update(query, update);
}
incUsersCountById(_id, inc = 1) {
const query = { _id };
const update = {
$inc: {
usersCount: inc,
},
};
return this.update(query, update);
}
incUsersCountByIds(ids, inc = 1) {
const query = {
_id: {
$in: ids,
},
};
const update = {
$inc: {
usersCount: inc,
},
};
return this.update(query, update, { multi: true });
}
incUsersCountNotDMsByIds(ids, inc = 1) {
const query = {
_id: {
$in: ids,
},
t: { $ne: 'd' },
};
const update = {
$inc: {
usersCount: inc,
},
};
return this.update(query, update, { multi: true });
}
}
export default new Rooms('room', true);

@ -239,8 +239,8 @@ export default class RocketAdapter {
return `slack-${slackChannel}-${ts.replace(/\./g, '-')}`;
}
findChannel(slackChannelId) {
return Rooms.findOneByImportId(slackChannelId);
async findChannel(slackChannelId) {
return RoomsRaw.findOneByImportId(slackChannelId);
}
getRocketUsers(members, slackChannel) {

@ -4,7 +4,7 @@ import https from 'https';
import { RTMClient } from '@slack/rtm-api';
import { Meteor } from 'meteor/meteor';
import { Messages as MessagesRaw } from '@rocket.chat/models';
import { Messages as MessagesRaw, Rooms as RoomsRaw } from '@rocket.chat/models';
import { Message } from '@rocket.chat/core-services';
import { slackLogger } from './logger';
@ -58,9 +58,9 @@ export default class SlackAdapter {
this.registerForEvents();
Meteor.startup(() => {
Meteor.startup(async () => {
try {
this.populateMembershipChannelMap(); // If run outside of Meteor.startup, HTTP is not defined
await this.populateMembershipChannelMap(); // If run outside of Meteor.startup, HTTP is not defined
} catch (err) {
slackLogger.error({ msg: 'Error attempting to connect to Slack', err });
this.slackBridge.disconnect();
@ -576,40 +576,42 @@ export default class SlackAdapter {
return this.slackChannelRocketBotMembershipMap.get(rocketChID);
}
populateMembershipChannelMapByChannels() {
async populateMembershipChannelMapByChannels() {
const channels = this.slackAPI.getChannels();
if (!channels || channels.length <= 0) {
return;
}
for (const slackChannel of channels) {
for await (const slackChannel of channels) {
const rocketchat_room =
Rooms.findOneByName(slackChannel.name, { fields: { _id: 1 } }) || Rooms.findOneByImportId(slackChannel.id, { fields: { _id: 1 } });
Rooms.findOneByName(slackChannel.name, { fields: { _id: 1 } }) ||
(await RoomsRaw.findOneByImportId(slackChannel.id, { projection: { _id: 1 } }));
if (rocketchat_room && slackChannel.is_member) {
this.addSlackChannel(rocketchat_room._id, slackChannel.id);
}
}
}
populateMembershipChannelMapByGroups() {
async populateMembershipChannelMapByGroups() {
const groups = this.slackAPI.getGroups();
if (!groups || groups.length <= 0) {
return;
}
for (const slackGroup of groups) {
for await (const slackGroup of groups) {
const rocketchat_room =
Rooms.findOneByName(slackGroup.name, { fields: { _id: 1 } }) || Rooms.findOneByImportId(slackGroup.id, { fields: { _id: 1 } });
Rooms.findOneByName(slackGroup.name, { fields: { _id: 1 } }) ||
(await RoomsRaw.findOneByImportId(slackGroup.id, { projection: { _id: 1 } }));
if (rocketchat_room && slackGroup.is_member) {
this.addSlackChannel(rocketchat_room._id, slackGroup.id);
}
}
}
populateMembershipChannelMap() {
async populateMembershipChannelMap() {
slackLogger.debug('Populating channel map');
this.populateMembershipChannelMapByChannels();
this.populateMembershipChannelMapByGroups();
await this.populateMembershipChannelMapByChannels();
await this.populateMembershipChannelMapByGroups();
}
/*

@ -1,18 +1,18 @@
import { Meteor } from 'meteor/meteor';
import limax from 'limax';
import { escapeHTML } from '@rocket.chat/string-helpers';
import { Rooms } from '@rocket.chat/models';
import { settings } from '../../../settings/server';
import { Rooms } from '../../../models/server';
import { validateName } from '../../../lib/server/functions/validateName';
export const getValidRoomName = (displayName, rid = '', options = {}) => {
export const getValidRoomName = async (displayName, rid = '', options = {}) => {
let slugifiedName = displayName;
if (settings.get('UI_Allow_room_names_with_special_chars')) {
const cleanName = limax(displayName, { maintainCase: true });
if (options.allowDuplicates !== true) {
const room = Rooms.findOneByDisplayName(displayName);
const room = await Rooms.findOneByDisplayName(displayName);
if (room && room._id !== rid) {
if (room.archived) {
throw new Meteor.Error('error-archived-duplicate-name', `There's an archived channel with name ${cleanName}`, {
@ -50,12 +50,13 @@ export const getValidRoomName = (displayName, rid = '', options = {}) => {
}
if (options.allowDuplicates !== true) {
const room = Rooms.findOneByName(slugifiedName);
const room = await Rooms.findOneByName(slugifiedName);
if (room && room._id !== rid) {
if (settings.get('UI_Allow_room_names_with_special_chars')) {
let tmpName = slugifiedName;
let next = 0;
while (Rooms.findOneByNameAndNotId(tmpName, rid)) {
// eslint-disable-next-line no-await-in-loop
while (await Rooms.findOneByNameAndNotId(tmpName, rid)) {
tmpName = `${slugifiedName}-${++next}`;
}
slugifiedName = tmpName;

@ -6,15 +6,15 @@ import { escapeRegExp } from '@rocket.chat/string-helpers';
import type { ILivechatAgent, ILivechatVisitor, IMessage, IRoom, IUser } from '@rocket.chat/core-typings';
import type { ServerMethods } from '@rocket.chat/ui-contexts';
import type { Mongo } from 'meteor/mongo';
import { LivechatRooms } from '@rocket.chat/models';
import { LivechatRooms, Rooms } from '@rocket.chat/models';
import AuditLog from './AuditLog';
import { Rooms, Messages, Users } from '../../../../app/models/server';
import { Messages, Users } from '../../../../app/models/server';
import { hasPermissionAsync } from '../../../../app/authorization/server/functions/hasPermission';
import { updateCounter } from '../../../../app/statistics/server';
import type { IAuditLog } from '../../../definition/IAuditLog';
const getValue = (room?: IRoom) => room && { rids: [room._id], name: room.name };
const getValue = (room: IRoom | null) => room && { rids: [room._id], name: room.name };
const getUsersIdFromUserName = (usernames: IUser['username'][]) => {
const user: IUser[] = usernames ? Users.findByUsername({ $in: usernames }) : undefined;
@ -30,16 +30,16 @@ const getRoomInfoByAuditParams = async ({
}: {
type: string;
roomId: IRoom['_id'];
users: IUser['username'][];
users: NonNullable<IUser['username']>[];
visitor: ILivechatVisitor['_id'];
agent: ILivechatAgent['_id'];
}) => {
if (rid) {
return getValue(Rooms.findOne({ _id: rid }));
return getValue(await Rooms.findOne({ _id: rid }));
}
if (type === 'd') {
return getValue(Rooms.findDirectRoomContainingAllUsernames(usernames));
return getValue(await Rooms.findDirectRoomContainingAllUsernames(usernames));
}
if (type === 'l') {
@ -59,7 +59,7 @@ declare module '@rocket.chat/ui-contexts' {
rid: IRoom['_id'];
startDate: Date;
endDate: Date;
users: IUser['username'][];
users: NonNullable<IUser['username']>[];
msg: IMessage['msg'];
type: string;
visitor: ILivechatVisitor['_id'];
@ -68,7 +68,7 @@ declare module '@rocket.chat/ui-contexts' {
auditGetOmnichannelMessages: (params: {
startDate: Date;
endDate: Date;
users: IUser['username'][];
users: NonNullable<IUser['username']>[];
msg: IMessage['msg'];
type: 'l';
visitor?: ILivechatVisitor['_id'];

@ -1,11 +1,10 @@
import type ldapjs from 'ldapjs';
import type { ILDAPEntry, IUser, IRoom, IRole, IImportUser } from '@rocket.chat/core-typings';
import { Users as UsersRaw, Roles, Subscriptions as SubscriptionsRaw } from '@rocket.chat/models';
import { Users as UsersRaw, Roles, Subscriptions as SubscriptionsRaw, Rooms } from '@rocket.chat/models';
import { Team } from '@rocket.chat/core-services';
import type { ImporterAfterImportCallback } from '../../../../app/importer/server/definitions/IConversionCallbacks';
import { settings } from '../../../../app/settings/server';
import { Rooms } from '../../../../app/models/server';
import { LDAPDataConverter } from '../../../../server/lib/ldap/DataConverter';
import { LDAPConnection } from '../../../../server/lib/ldap/Connection';
import { LDAPManager } from '../../../../server/lib/ldap/Manager';
@ -14,6 +13,7 @@ import { addUserToRoom, removeUserFromRoom, createRoom } from '../../../../app/l
import { syncUserRoles } from '../syncUserRoles';
import { ensureArray } from '../../../../lib/utils/arrayUtils';
import { copyCustomFieldsLDAP } from './copyCustomFieldsLDAP';
import { getValidRoomName } from '../../../../app/utils/server';
export class LDAPEEManager extends LDAPManager {
public static async sync(): Promise<void> {
@ -302,7 +302,8 @@ export class LDAPEEManager extends LDAPManager {
const channels: Array<string> = [].concat(fieldMap[ldapField]);
for await (const channel of channels) {
try {
const room: IRoom | undefined = Rooms.findOneByNonValidatedName(channel) || (await this.createRoomForSync(channel));
const name = await getValidRoomName(channel.trim(), undefined, { allowDuplicates: true });
const room = (await Rooms.findOneByNonValidatedName(name)) || (await this.createRoomForSync(channel));
if (!room) {
return;
}

@ -1,10 +1,10 @@
import { Roles } from '@rocket.chat/models';
import { Roles, Rooms } from '@rocket.chat/models';
import type { IUser } from '@rocket.chat/core-typings';
import { Rooms } from '../../../../app/models/server';
import { addUserToRoom, createRoom } from '../../../../app/lib/server/functions';
import { Logger } from '../../../../app/logger/server';
import { syncUserRoles } from '../syncUserRoles';
import { getValidRoomName } from '../../../../app/utils/server';
const logger = new Logger('OAuth');
@ -26,15 +26,19 @@ export class OAuthEEManager {
channels = [channels];
}
for await (const channel of channels) {
let room = Rooms.findOneByNonValidatedName(channel);
const name = await getValidRoomName(channel.trim(), undefined, { allowDuplicates: true });
let room = await Rooms.findOneByNonValidatedName(name);
if (!room) {
room = await createRoom('c', channel, channelsAdmin, [], false);
if (!room?.rid) {
const createdRoom = await createRoom('c', channel, channelsAdmin, [], false);
if (!createdRoom?.rid) {
logger.error(`could not create channel ${channel}`);
return;
}
room = createdRoom;
}
if (Array.isArray(groupsFromSSO) && groupsFromSSO.includes(ssoGroup)) {
if (room && Array.isArray(groupsFromSSO) && groupsFromSSO.includes(ssoGroup)) {
await addUserToRoom(room._id, user);
}
}

@ -3,10 +3,11 @@ import { check, Match } from 'meteor/check';
import type { ServerMethods } from '@rocket.chat/ui-contexts';
import type { ICreatedRoom, IUser } from '@rocket.chat/core-typings';
import type { ICreateRoomParams } from '@rocket.chat/core-services';
import { Rooms } from '@rocket.chat/models';
import { settings } from '../../app/settings/server';
import { hasPermissionAsync } from '../../app/authorization/server/functions/hasPermission';
import { Users, Rooms } from '../../app/models/server';
import { Users } from '../../app/models/server';
import { RateLimiterClass as RateLimiter } from '../../app/lib/server/lib/RateLimiter';
import { createRoom } from '../../app/lib/server/functions/createRoom';
import { addUser } from '../../app/federation/server/functions/addUser';
@ -80,12 +81,12 @@ export async function createDirectMessage(
if (await hasPermissionAsync(userId, 'view-d-room')) {
// Check if the direct room already exists, then return it
const uids = roomUsers.map(({ _id }) => _id).sort();
const room = Rooms.findOneDirectRoomContainingAllUserIDs(uids, { fields: { _id: 1 } });
const room = await Rooms.findOneDirectRoomContainingAllUserIDs(uids, { projection: { _id: 1 } });
if (room) {
return {
...room,
t: 'd',
rid: room._id,
...room,
};
}
}

@ -493,6 +493,30 @@ export class RoomsRaw extends BaseRaw {
return this.col.aggregate(params, { allowDiskUse: true, readPreference });
}
findOneByNameOrFname(name, options) {
const query = {
$or: [
{
name,
},
{
fname: name,
},
],
};
return this.findOne(query, options);
}
async findOneByNonValidatedName(name, options) {
const room = await this.findOneByNameOrFname(name, options);
if (room) {
return room;
}
return this.findOneByName(name, options);
}
findOneByName(name, options = {}) {
return this.col.findOne({ name }, options);
}
@ -523,10 +547,6 @@ export class RoomsRaw extends BaseRaw {
return this.updateMany(query, update);
}
findOneByNameOrFname(name, options = {}) {
return this.col.findOne({ $or: [{ name }, { fname: name }] }, options);
}
allRoomSourcesCount() {
return this.col.aggregate([
{

@ -33,7 +33,7 @@ Meteor.methods<ServerMethods>({
if (!user) {
if (settings.get('Accounts_AllowAnonymousRead')) {
return Rooms.findByDefaultAndTypes(true, ['c'], options).fetch();
return RoomsRaw.findByDefaultAndTypes(true, ['c'], options).toArray();
}
return [];
}

@ -67,6 +67,7 @@ export interface IRoomsModel extends IBaseModel<IRoom> {
incUsersCountByIds(ids: any, inc: number): any;
findOneByNameOrFname(name: any, options?: any): any;
findOneByNonValidatedName(name: string, options?: FindOptions<IRoom>): Promise<IRoom | null>;
allRoomSourcesCount(): AggregationCursor<Document>; // TODO change back when convert model do TS AggregationCursor<{ _id: Required<IOmnichannelGenericRoom['source']>; count: number }>;
@ -167,7 +168,7 @@ export interface IRoomsModel extends IBaseModel<IRoom> {
includeFederatedRooms?: boolean,
): FindCursor<IRoom>;
findByDefaultAndTypes(defaultValue: boolean, types: IRoom['t'][], options?: FindOptions<IRoom>): FindCursor<IRoom>;
findDirectRoomContainingAllUsernames(usernames: string[], options?: FindOptions<IRoom>): FindCursor<IRoom>;
findDirectRoomContainingAllUsernames(usernames: string[], options?: FindOptions<IRoom>): Promise<IRoom | null>;
findByTypeAndName(type: IRoom['t'], name: string, options?: FindOptions<IRoom>): Promise<IRoom | null>;
findByTypeAndNameOrId(type: IRoom['t'], name: string, options?: FindOptions<IRoom>): Promise<IRoom | null>;
findByTypeAndNameContaining(type: IRoom['t'], name: string, options?: FindOptions<IRoom>): FindCursor<IRoom>;

Loading…
Cancel
Save