snapshot of https://github.com/RocketChat/Rocket.Chat.git (develop, 2026-09-16; blobs > 131072B omitted)
Some checks failed
Create issues from TODOs / todos (push) Failing after 3s
Close inactive issues / close-issues (push) Has been cancelled
Deploy GitHub Pages / deploy-preview (push) Has been cancelled
CI / ⚙️ Variables Setup (push) Has been cancelled
CI / ⚙️ Test Guard (push) Has been cancelled
CI / 🚀 Notify external services - draft (push) Has been cancelled
CI / 📦 Build Packages (push) Has been cancelled
CI / 📦 Meteor Build (coverage) (push) Has been cancelled
CI / 📦 Meteor Build (production) (push) Has been cancelled
CI / 🚢 Build Docker (amd64, [account-service presence-service omnichannel-transcript-service], ${{ (github.event_name != 'release' && github.ref != 'refs/heads/develop') && 'coverage' || 'production' }}) (push) Has been cancelled
CI / 🚢 Build Docker (amd64, [account-service presence-service omnichannel-transcript-service], fips) (push) Has been cancelled
CI / 🚢 Build Docker (amd64, [authorization-service queue-worker-service ddp-streamer-service], ${{ (github.event_name != 'release' && github.ref != 'refs/heads/develop') && 'coverage' || 'production' }}) (push) Has been cancelled
CI / 🚢 Build Docker (amd64, [authorization-service queue-worker-service ddp-streamer-service], fips) (push) Has been cancelled
CI / 🚢 Build Docker (amd64, [rocketchat], ${{ (github.event_name != 'release' && github.ref != 'refs/heads/develop') && 'coverage' || 'production' }}) (push) Has been cancelled
CI / 🚢 Build Docker (amd64, [rocketchat], coverage) (push) Has been cancelled
CI / 🚢 Build Docker (amd64, [rocketchat], fips) (push) Has been cancelled
CI / 🚢 Build Docker (arm64, [account-service presence-service omnichannel-transcript-service], ${{ (github.event_name != 'release' && github.ref != 'refs/heads/develop') && 'coverage' || 'production' }}) (push) Has been cancelled
CI / 🚢 Build Docker (arm64, [authorization-service queue-worker-service ddp-streamer-service], ${{ (github.event_name != 'release' && github.ref != 'refs/heads/develop') && 'coverage' || 'production' }}) (push) Has been cancelled
CI / 🚢 Build Docker (arm64, [rocketchat], ${{ (github.event_name != 'release' && github.ref != 'refs/heads/develop') && 'coverage' || 'production' }}) (push) Has been cancelled
CI / 🚢 Build Docker (arm64, [rocketchat], coverage) (push) Has been cancelled
CI / 🚢 Publish Docker Images (ghcr.io) (push) Has been cancelled
CI / 📦 Track Image Sizes (push) Has been cancelled
CI / 🔎 Code Check - Action Lint (push) Has been cancelled
CI / 🔎 Code Check (push) Has been cancelled
CI / 🔨 Test Storybook (push) Has been cancelled
CI / 🔨 Test Unit (push) Has been cancelled
CI / 🔨 Test API (CE) (push) Has been cancelled
CI / 🔨 Test API Livechat (CE) (push) Has been cancelled
CI / 🔨 Test UI (CE) (push) Has been cancelled
CI / 🔨 Test API (EE) (push) Has been cancelled
CI / 🔨 Test API Livechat (EE) (push) Has been cancelled
CI / 🔨 Test API Apps (deno-runtime - EE) (push) Has been cancelled
CI / 🔨 Test UI (EE) (push) Has been cancelled
CI / 🔨 Test API (FIPS) (push) Has been cancelled
CI / 🔨 Test API Livechat (FIPS) (push) Has been cancelled
CI / 🔨 Test UI (FIPS) (push) Has been cancelled
CI / 🔨 Test Federation Matrix (push) Has been cancelled
CI / 📊 Report Coverage (push) Has been cancelled
CI / ✅ Tests Done (push) Has been cancelled
CI / 🚀 Publish build assets (push) Has been cancelled
CI / 🚀 Publish Docker Images (DockerHub) (push) Has been cancelled
CI / 🚀 Notify external services (push) Has been cancelled
CI / Update Version Durability (push) Has been cancelled
Release / Release (push) Has been cancelled
Auto-close duplicate issues / auto-close-duplicates (push) Failing after 1s
Code scanning - action / CodeQL-Build (push) Failing after 1s
Some checks failed
Create issues from TODOs / todos (push) Failing after 3s
Close inactive issues / close-issues (push) Has been cancelled
Deploy GitHub Pages / deploy-preview (push) Has been cancelled
CI / ⚙️ Variables Setup (push) Has been cancelled
CI / ⚙️ Test Guard (push) Has been cancelled
CI / 🚀 Notify external services - draft (push) Has been cancelled
CI / 📦 Build Packages (push) Has been cancelled
CI / 📦 Meteor Build (coverage) (push) Has been cancelled
CI / 📦 Meteor Build (production) (push) Has been cancelled
CI / 🚢 Build Docker (amd64, [account-service presence-service omnichannel-transcript-service], ${{ (github.event_name != 'release' && github.ref != 'refs/heads/develop') && 'coverage' || 'production' }}) (push) Has been cancelled
CI / 🚢 Build Docker (amd64, [account-service presence-service omnichannel-transcript-service], fips) (push) Has been cancelled
CI / 🚢 Build Docker (amd64, [authorization-service queue-worker-service ddp-streamer-service], ${{ (github.event_name != 'release' && github.ref != 'refs/heads/develop') && 'coverage' || 'production' }}) (push) Has been cancelled
CI / 🚢 Build Docker (amd64, [authorization-service queue-worker-service ddp-streamer-service], fips) (push) Has been cancelled
CI / 🚢 Build Docker (amd64, [rocketchat], ${{ (github.event_name != 'release' && github.ref != 'refs/heads/develop') && 'coverage' || 'production' }}) (push) Has been cancelled
CI / 🚢 Build Docker (amd64, [rocketchat], coverage) (push) Has been cancelled
CI / 🚢 Build Docker (amd64, [rocketchat], fips) (push) Has been cancelled
CI / 🚢 Build Docker (arm64, [account-service presence-service omnichannel-transcript-service], ${{ (github.event_name != 'release' && github.ref != 'refs/heads/develop') && 'coverage' || 'production' }}) (push) Has been cancelled
CI / 🚢 Build Docker (arm64, [authorization-service queue-worker-service ddp-streamer-service], ${{ (github.event_name != 'release' && github.ref != 'refs/heads/develop') && 'coverage' || 'production' }}) (push) Has been cancelled
CI / 🚢 Build Docker (arm64, [rocketchat], ${{ (github.event_name != 'release' && github.ref != 'refs/heads/develop') && 'coverage' || 'production' }}) (push) Has been cancelled
CI / 🚢 Build Docker (arm64, [rocketchat], coverage) (push) Has been cancelled
CI / 🚢 Publish Docker Images (ghcr.io) (push) Has been cancelled
CI / 📦 Track Image Sizes (push) Has been cancelled
CI / 🔎 Code Check - Action Lint (push) Has been cancelled
CI / 🔎 Code Check (push) Has been cancelled
CI / 🔨 Test Storybook (push) Has been cancelled
CI / 🔨 Test Unit (push) Has been cancelled
CI / 🔨 Test API (CE) (push) Has been cancelled
CI / 🔨 Test API Livechat (CE) (push) Has been cancelled
CI / 🔨 Test UI (CE) (push) Has been cancelled
CI / 🔨 Test API (EE) (push) Has been cancelled
CI / 🔨 Test API Livechat (EE) (push) Has been cancelled
CI / 🔨 Test API Apps (deno-runtime - EE) (push) Has been cancelled
CI / 🔨 Test UI (EE) (push) Has been cancelled
CI / 🔨 Test API (FIPS) (push) Has been cancelled
CI / 🔨 Test API Livechat (FIPS) (push) Has been cancelled
CI / 🔨 Test UI (FIPS) (push) Has been cancelled
CI / 🔨 Test Federation Matrix (push) Has been cancelled
CI / 📊 Report Coverage (push) Has been cancelled
CI / ✅ Tests Done (push) Has been cancelled
CI / 🚀 Publish build assets (push) Has been cancelled
CI / 🚀 Publish Docker Images (DockerHub) (push) Has been cancelled
CI / 🚀 Notify external services (push) Has been cancelled
CI / Update Version Durability (push) Has been cancelled
Release / Release (push) Has been cancelled
Auto-close duplicate issues / auto-close-duplicates (push) Failing after 1s
Code scanning - action / CodeQL-Build (push) Failing after 1s
This commit is contained in:
287
apps/meteor/server/lib/spotlight.js
Normal file
287
apps/meteor/server/lib/spotlight.js
Normal file
@@ -0,0 +1,287 @@
|
||||
import { Team } from '@rocket.chat/core-services';
|
||||
import { Users, Subscriptions as SubscriptionsRaw, Rooms } from '@rocket.chat/models';
|
||||
import { escapeRegExp } from '@rocket.chat/tools';
|
||||
|
||||
import { canAccessRoomAsync, roomAccessAttributes } from './authorization';
|
||||
import { settings } from '../settings';
|
||||
import { hasPermissionAsync, hasAllPermissionAsync } from './authorization/hasPermission';
|
||||
import { roomCoordinator } from './rooms/roomCoordinator';
|
||||
import { trim } from '../../lib/utils/stringUtils';
|
||||
import { readSecondaryPreferred } from '../database/readSecondaryPreferred';
|
||||
|
||||
export class Spotlight {
|
||||
async fetchRooms(userId, rooms) {
|
||||
if (!settings.get('Store_Last_Message') || (userId && (await hasPermissionAsync(userId, 'preview-c-room')))) {
|
||||
return rooms;
|
||||
}
|
||||
|
||||
return rooms.map((room) => {
|
||||
delete room.lastMessage;
|
||||
return room;
|
||||
});
|
||||
}
|
||||
|
||||
async searchRooms({ userId, text, includeFederatedRooms = false }) {
|
||||
const regex = new RegExp(trim(escapeRegExp(text)), 'i');
|
||||
|
||||
const roomOptions = {
|
||||
limit: 5,
|
||||
projection: {
|
||||
t: 1,
|
||||
name: 1,
|
||||
fname: 1,
|
||||
teamMain: 1,
|
||||
joinCodeRequired: 1,
|
||||
lastMessage: 1,
|
||||
federated: true,
|
||||
prid: 1,
|
||||
},
|
||||
sort: {
|
||||
name: 1,
|
||||
},
|
||||
readPreference: readSecondaryPreferred(Rooms.col.s.db),
|
||||
};
|
||||
|
||||
if (userId == null) {
|
||||
if (!settings.get('Accounts_AllowAnonymousRead')) {
|
||||
return [];
|
||||
}
|
||||
|
||||
return this.fetchRooms(userId, await Rooms.findByNameAndTypeNotDefault(regex, 'c', roomOptions, includeFederatedRooms).toArray());
|
||||
}
|
||||
|
||||
if (!(await hasAllPermissionAsync(userId, ['view-outside-room', 'view-c-room']))) {
|
||||
return [];
|
||||
}
|
||||
|
||||
const searchableRoomTypeIds = roomCoordinator.searchableRoomTypes();
|
||||
|
||||
const [subscriptions, exactRoom] = await Promise.all([
|
||||
SubscriptionsRaw.findByUserIdAndTypes(userId, searchableRoomTypeIds, {
|
||||
projection: { rid: 1 },
|
||||
readPreference: roomOptions.readPreference,
|
||||
}).toArray(),
|
||||
Rooms.findOneByNameAndType(text, searchableRoomTypeIds, roomOptions, includeFederatedRooms),
|
||||
]);
|
||||
|
||||
const roomIds = subscriptions.map((s) => s.rid);
|
||||
if (exactRoom) {
|
||||
roomIds.push(exactRoom.rid);
|
||||
}
|
||||
|
||||
return this.fetchRooms(
|
||||
userId,
|
||||
await Rooms.findByNameOrFNameAndTypesNotInIds(regex, searchableRoomTypeIds, roomIds, roomOptions, includeFederatedRooms).toArray(),
|
||||
);
|
||||
}
|
||||
|
||||
mapOutsiders(u) {
|
||||
u.outside = true;
|
||||
return u;
|
||||
}
|
||||
|
||||
processLimitAndUsernames(options, usernames, users) {
|
||||
// Reduce the results from the limit for the next query
|
||||
options.limit -= users.length;
|
||||
|
||||
// If the limit was reached, return
|
||||
if (options.limit <= 0) {
|
||||
return users;
|
||||
}
|
||||
|
||||
// Prevent the next query to get the same users
|
||||
usernames.push(...users.map((u) => u.username).filter((u) => !usernames.includes(u)));
|
||||
}
|
||||
|
||||
async _searchInsiderUsers({ rid, text, usernames, options, users, insiderExtraQuery, match = { startsWith: false, endsWith: false } }) {
|
||||
// Get insiders first
|
||||
if (rid) {
|
||||
const searchFields = settings.get('Accounts_SearchFields').trim().split(',');
|
||||
|
||||
users.push(...(await Users.findByActiveUsersExcept(text, usernames, options, searchFields, insiderExtraQuery, match).toArray()));
|
||||
|
||||
// If the limit was reached, return
|
||||
if (this.processLimitAndUsernames(options, usernames, users)) {
|
||||
return users;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async _searchConnectedUsers(userId, { text, usernames, options, users, match = { startsWith: false, endsWith: false } }, roomType) {
|
||||
const searchFields = settings.get('Accounts_SearchFields').trim().split(',');
|
||||
|
||||
users.push(
|
||||
...(
|
||||
await SubscriptionsRaw.findConnectedUsersExcept(userId, text, usernames, searchFields, {}, options.limit || 5, roomType, match, {
|
||||
readPreference: options.readPreference,
|
||||
})
|
||||
).map(this.mapOutsiders),
|
||||
);
|
||||
|
||||
// If the limit was reached, return
|
||||
if (this.processLimitAndUsernames(options, usernames, users)) {
|
||||
return users;
|
||||
}
|
||||
}
|
||||
|
||||
async _searchOutsiderUsers({ text, usernames, options, users, canListOutsiders, match = { startsWith: false, endsWith: false } }) {
|
||||
// Then get the outsiders if allowed
|
||||
if (canListOutsiders) {
|
||||
const searchFields = settings.get('Accounts_SearchFields').trim().split(',');
|
||||
users.push(
|
||||
...(await Users.findByActiveUsersExcept(text, usernames, options, searchFields, undefined, match).toArray()).map(this.mapOutsiders),
|
||||
);
|
||||
|
||||
// If the limit was reached, return
|
||||
if (this.processLimitAndUsernames(options, usernames, users)) {
|
||||
return users;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
mapTeams(teams) {
|
||||
return teams.map((t) => {
|
||||
t.isTeam = true;
|
||||
t.username = t.name;
|
||||
t.status = 'online';
|
||||
return t;
|
||||
});
|
||||
}
|
||||
|
||||
async _searchTeams(userId, { text, options, users, mentions }) {
|
||||
if (!mentions || settings.get('Troubleshoot_Disable_Teams_Mention')) {
|
||||
return users;
|
||||
}
|
||||
|
||||
options.limit -= users.length;
|
||||
|
||||
if (options.limit <= 0) {
|
||||
return users;
|
||||
}
|
||||
|
||||
const teamOptions = { ...options, projection: { name: 1, type: 1 } };
|
||||
const teams = await Team.search(userId, text, teamOptions);
|
||||
users.push(...this.mapTeams(teams));
|
||||
|
||||
return users;
|
||||
}
|
||||
|
||||
async searchUsers({ userId, rid, text, usernames, mentions }) {
|
||||
const users = [];
|
||||
|
||||
const options = {
|
||||
limit: settings.get('Number_of_users_autocomplete_suggestions'),
|
||||
projection: {
|
||||
username: 1,
|
||||
nickname: 1,
|
||||
name: 1,
|
||||
status: 1,
|
||||
statusText: 1,
|
||||
avatarETag: 1,
|
||||
},
|
||||
sort: {
|
||||
[settings.get('UI_Use_Real_Name') ? 'name' : 'username']: 1,
|
||||
},
|
||||
readPreference: readSecondaryPreferred(Users.col.s.db),
|
||||
};
|
||||
|
||||
const room = await Rooms.findOneById(rid, { projection: { ...roomAccessAttributes, _id: 1, t: 1, uids: 1 } });
|
||||
|
||||
if (rid && !room) {
|
||||
return users;
|
||||
}
|
||||
|
||||
const canListOutsiders = !!userId && (await hasAllPermissionAsync(userId, ['view-outside-room', 'view-d-room']));
|
||||
const canListInsiders = canListOutsiders || (rid && (await canAccessRoomAsync(room, { _id: userId })));
|
||||
|
||||
const insiderExtraQuery = [];
|
||||
|
||||
if (rid) {
|
||||
switch (room.t) {
|
||||
case 'd':
|
||||
insiderExtraQuery.push({
|
||||
_id: { $in: room.uids.filter((id) => id !== userId) },
|
||||
});
|
||||
break;
|
||||
case 'l':
|
||||
insiderExtraQuery.push({
|
||||
_id: {
|
||||
$in: (await SubscriptionsRaw.findByRoomId(room._id).toArray()).map((s) => s.u?._id).filter((id) => id && id !== userId),
|
||||
},
|
||||
});
|
||||
break;
|
||||
default:
|
||||
insiderExtraQuery.push({
|
||||
__rooms: rid,
|
||||
});
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
const searchParams = {
|
||||
rid,
|
||||
text,
|
||||
usernames,
|
||||
options,
|
||||
users,
|
||||
canListOutsiders,
|
||||
insiderExtraQuery,
|
||||
mentions,
|
||||
};
|
||||
|
||||
// Exact match for username only
|
||||
// TODO: these exact-match branches push the user without filtering against `usernames`
|
||||
// (the exclusion list), so an exact username query bypasses the exclusion that the
|
||||
// findByActiveUsersExcept paths below honor. Evaluate filtering exactMatch against
|
||||
// `usernames` here so the exclusion applies uniformly.
|
||||
if (rid && canListInsiders) {
|
||||
const exactMatch = await Users.findOneByUsernameAndRoomIgnoringCase(text, rid, {
|
||||
projection: options.projection,
|
||||
readPreference: options.readPreference,
|
||||
});
|
||||
if (exactMatch) {
|
||||
users.push(exactMatch);
|
||||
this.processLimitAndUsernames(options, usernames, users);
|
||||
}
|
||||
}
|
||||
|
||||
if (users.length === 0 && canListOutsiders && text) {
|
||||
const exactMatch = await Users.findOneByUsernameIgnoringCase(text, {
|
||||
projection: options.projection,
|
||||
readPreference: options.readPreference,
|
||||
});
|
||||
if (exactMatch) {
|
||||
users.push(this.mapOutsiders(exactMatch));
|
||||
this.processLimitAndUsernames(options, usernames, users);
|
||||
}
|
||||
}
|
||||
|
||||
if (canListInsiders && rid) {
|
||||
// Search for insiders
|
||||
if (await this._searchInsiderUsers(searchParams)) {
|
||||
return users;
|
||||
}
|
||||
|
||||
// Search for users that the requester has DMs with
|
||||
if (await this._searchConnectedUsers(userId, searchParams, 'd')) {
|
||||
return users;
|
||||
}
|
||||
}
|
||||
|
||||
// If the user can search outsiders, search for any user in the server
|
||||
// Otherwise, search for users that are subscribed to the same rooms as the requester
|
||||
if (canListOutsiders) {
|
||||
if (await this._searchOutsiderUsers(searchParams)) {
|
||||
return users;
|
||||
}
|
||||
} else if (await this._searchConnectedUsers(userId, searchParams, 'd')) {
|
||||
return users;
|
||||
}
|
||||
|
||||
if (await this._searchTeams(userId, searchParams)) {
|
||||
return users;
|
||||
}
|
||||
|
||||
return users;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user