>;
+
+export function getUsersInRole(name: IRole['name'], scope: string | undefined, options: FindOneOptions
): Promise>;
+
+export function getUsersInRole(name: IRole['name'], scope: string | undefined, options?: any | undefined): Promise> { return Roles.findUsersInRole(name, scope, options); }
diff --git a/app/authorization/server/functions/removeUserFromRoles.js b/app/authorization/server/functions/removeUserFromRoles.js
index b08c2778addb..a55d722bb891 100644
--- a/app/authorization/server/functions/removeUserFromRoles.js
+++ b/app/authorization/server/functions/removeUserFromRoles.js
@@ -2,7 +2,8 @@ import { Meteor } from 'meteor/meteor';
import _ from 'underscore';
import { getRoles } from './getRoles';
-import { Users, Roles } from '../../../models';
+import { Users } from '../../../models/server';
+import { Roles } from '../../../models/server/raw';
export const removeUserFromRoles = (userId, roleNames, scope) => {
if (!userId || !roleNames) {
@@ -27,7 +28,7 @@ export const removeUserFromRoles = (userId, roleNames, scope) => {
});
}
- Roles.removeUserRoles(userId, roleNames, scope);
+ Promise.await(Roles.removeUserRoles(userId, roleNames, scope));
return true;
};
diff --git a/app/authorization/server/functions/upsertPermissions.js b/app/authorization/server/functions/upsertPermissions.ts
similarity index 86%
rename from app/authorization/server/functions/upsertPermissions.js
rename to app/authorization/server/functions/upsertPermissions.ts
index 76e97fcef480..6c41b1b11af0 100644
--- a/app/authorization/server/functions/upsertPermissions.js
+++ b/app/authorization/server/functions/upsertPermissions.ts
@@ -1,11 +1,11 @@
/* eslint no-multi-spaces: 0 */
-import Roles from '../../../models/server/models/Roles';
-import Permissions from '../../../models/server/models/Permissions';
-import Settings from '../../../models/server/models/Settings';
import { settings } from '../../../settings/server';
import { getSettingPermissionId, CONSTANTS } from '../../lib';
+import { Permissions, Roles, Settings } from '../../../models/server/raw';
+import { IPermission } from '../../../../definition/IPermission';
+import { ISetting } from '../../../../definition/ISetting';
-export const upsertPermissions = () => {
+export const upsertPermissions = async (): Promise => {
// Note:
// 1.if we need to create a role that can only edit channel message, but not edit group message
// then we can define edit--message instead of edit-message
@@ -94,6 +94,7 @@ export const upsertPermissions = () => {
{ _id: 'create-invite-links', roles: ['admin', 'owner', 'moderator'] },
{ _id: 'view-l-room', roles: ['livechat-manager', 'livechat-monitor', 'livechat-agent', 'admin'] },
{ _id: 'view-livechat-manager', roles: ['livechat-manager', 'livechat-monitor', 'admin'] },
+ { _id: 'view-omnichannel-contact-center', roles: ['livechat-manager', 'livechat-agent', 'livechat-monitor', 'admin'] },
{ _id: 'edit-omnichannel-contact', roles: ['livechat-manager', 'livechat-agent', 'admin'] },
{ _id: 'view-livechat-rooms', roles: ['livechat-manager', 'livechat-monitor', 'admin'] },
{ _id: 'close-livechat-room', roles: ['livechat-manager', 'livechat-monitor', 'livechat-agent', 'admin'] },
@@ -149,11 +150,13 @@ export const upsertPermissions = () => {
{ _id: 'access-mailer', roles: ['admin'] },
{ _id: 'pin-message', roles: ['owner', 'moderator', 'admin'] },
{ _id: 'snippet-message', roles: ['owner', 'moderator', 'admin'] },
+ { _id: 'mobile-upload-file', roles: ['user', 'admin'] },
+ { _id: 'mobile-download-file', roles: ['user', 'admin'] },
];
- for (const permission of permissions) {
- Permissions.create(permission._id, permission.roles);
+ for await (const permission of permissions) {
+ await Permissions.create(permission._id, permission.roles);
}
const defaultRoles = [
@@ -170,29 +173,30 @@ export const upsertPermissions = () => {
{ name: 'livechat-manager', scope: 'Users', description: 'Livechat Manager' },
];
- for (const role of defaultRoles) {
- Roles.createOrUpdate(role.name, role.scope, role.description, true, false);
+ for await (const role of defaultRoles) {
+ await Roles.createOrUpdate(role.name, role.scope as 'Users' | 'Subscriptions', role.description, true, false);
}
- const getPreviousPermissions = function(settingId) {
- const previousSettingPermissions = {};
+ const getPreviousPermissions = async function(settingId?: string): Promise> {
+ const previousSettingPermissions: {
+ [key: string]: IPermission;
+ } = {};
- const selector = { level: CONSTANTS.SETTINGS_LEVEL };
- if (settingId) {
- selector.settingId = settingId;
- }
+ const selector = { level: 'settings' as const, ...settingId && { settingId } };
- Permissions.find(selector).forEach(
- function(permission) {
+ await Permissions.find(selector).forEach(
+ function(permission: IPermission) {
previousSettingPermissions[permission._id] = permission;
});
return previousSettingPermissions;
};
- const createSettingPermission = function(setting, previousSettingPermissions) {
+ const createSettingPermission = async function(setting: ISetting, previousSettingPermissions: {
+ [key: string]: IPermission;
+ }): Promise {
const permissionId = getSettingPermissionId(setting._id);
- const permission = {
- level: CONSTANTS.SETTINGS_LEVEL,
+ const permission: Omit = {
+ level: CONSTANTS.SETTINGS_LEVEL as 'settings' | undefined,
// copy those setting-properties which are needed to properly publish the setting-based permissions
settingId: setting._id,
group: setting.group,
@@ -211,19 +215,19 @@ export const upsertPermissions = () => {
permission.sectionPermissionId = getSettingPermissionId(setting.section);
}
- const existent = Permissions.findOne({
+ const existent = await Permissions.findOne({
_id: permissionId,
...permission,
}, { fields: { _id: 1 } });
if (!existent) {
try {
- Permissions.upsert({ _id: permissionId }, { $set: permission });
+ await Permissions.update({ _id: permissionId }, { $set: permission }, { upsert: true });
} catch (e) {
if (!e.message.includes('E11000')) {
// E11000 refers to a MongoDB error that can occur when using unique indexes for upserts
// https://docs.mongodb.com/manual/reference/method/db.collection.update/#use-unique-indexes
- Permissions.upsert({ _id: permissionId }, { $set: permission });
+ await Permissions.update({ _id: permissionId }, { $set: permission }, { upsert: true });
}
}
}
@@ -231,17 +235,17 @@ export const upsertPermissions = () => {
delete previousSettingPermissions[permissionId];
};
- const createPermissionsForExistingSettings = function() {
- const previousSettingPermissions = getPreviousPermissions();
+ const createPermissionsForExistingSettings = async function(): Promise {
+ const previousSettingPermissions = await getPreviousPermissions();
- Settings.findNotHidden().fetch().forEach((setting) => {
+ (await Settings.findNotHidden().toArray()).forEach((setting) => {
createSettingPermission(setting, previousSettingPermissions);
});
// remove permissions for non-existent settings
- for (const obsoletePermission in previousSettingPermissions) {
+ for await (const obsoletePermission of Object.keys(previousSettingPermissions)) {
if (previousSettingPermissions.hasOwnProperty(obsoletePermission)) {
- Permissions.remove({ _id: obsoletePermission });
+ await Permissions.deleteOne({ _id: obsoletePermission });
}
}
};
@@ -250,9 +254,9 @@ export const upsertPermissions = () => {
createPermissionsForExistingSettings();
// register a callback for settings for be create in higher-level-packages
- settings.on('*', function([settingId]) {
- const previousSettingPermissions = getPreviousPermissions(settingId);
- const setting = Settings.findOneById(settingId);
+ settings.on('*', async function([settingId]) {
+ const previousSettingPermissions = await getPreviousPermissions(settingId);
+ const setting = await Settings.findOneById(settingId);
if (setting) {
if (!setting.hidden) {
createSettingPermission(setting, previousSettingPermissions);
diff --git a/app/authorization/server/methods/addPermissionToRole.js b/app/authorization/server/methods/addPermissionToRole.ts
similarity index 72%
rename from app/authorization/server/methods/addPermissionToRole.js
rename to app/authorization/server/methods/addPermissionToRole.ts
index 5ca74ed3dbc9..42990b114437 100644
--- a/app/authorization/server/methods/addPermissionToRole.js
+++ b/app/authorization/server/methods/addPermissionToRole.ts
@@ -1,11 +1,12 @@
import { Meteor } from 'meteor/meteor';
-import { Permissions } from '../../../models/server';
+
import { hasPermission } from '../functions/hasPermission';
import { CONSTANTS, AuthorizationUtils } from '../../lib';
+import { Permissions } from '../../../models/server/raw';
Meteor.methods({
- 'authorization:addPermissionToRole'(permissionId, role) {
+ async 'authorization:addPermissionToRole'(permissionId, role) {
if (AuthorizationUtils.isPermissionRestrictedForRole(permissionId, role)) {
throw new Meteor.Error('error-action-not-allowed', 'Permission is restricted', {
method: 'authorization:addPermissionToRole',
@@ -14,7 +15,14 @@ Meteor.methods({
}
const uid = Meteor.userId();
- const permission = Permissions.findOneById(permissionId);
+ const permission = await Permissions.findOneById(permissionId);
+
+ if (!permission) {
+ throw new Meteor.Error('error-invalid-permission', 'Permission does not exist', {
+ method: 'authorization:addPermissionToRole',
+ action: 'Adding_permission',
+ });
+ }
if (!uid || !hasPermission(uid, 'access-permissions') || (permission.level === CONSTANTS.SETTINGS_LEVEL && !hasPermission(uid, 'access-setting-permissions'))) {
throw new Meteor.Error('error-action-not-allowed', 'Adding permission is not allowed', {
diff --git a/app/authorization/server/methods/addUserToRole.js b/app/authorization/server/methods/addUserToRole.ts
similarity index 84%
rename from app/authorization/server/methods/addUserToRole.js
rename to app/authorization/server/methods/addUserToRole.ts
index a7fdd21ec24d..3182d327ff47 100644
--- a/app/authorization/server/methods/addUserToRole.js
+++ b/app/authorization/server/methods/addUserToRole.ts
@@ -1,13 +1,14 @@
import { Meteor } from 'meteor/meteor';
import _ from 'underscore';
-import { Users, Roles } from '../../../models/server';
+import { Users } from '../../../models/server';
import { settings } from '../../../settings/server';
import { hasPermission } from '../functions/hasPermission';
import { api } from '../../../../server/sdk/api';
+import { Roles } from '../../../models/server/raw';
Meteor.methods({
- 'authorization:addUserToRole'(roleName, username, scope) {
+ async 'authorization:addUserToRole'(roleName, username, scope) {
if (!Meteor.userId() || !hasPermission(Meteor.userId(), 'access-permissions')) {
throw new Meteor.Error('error-action-not-allowed', 'Accessing permissions is not allowed', {
method: 'authorization:addUserToRole',
@@ -41,13 +42,13 @@ Meteor.methods({
}
// verify if user can be added to given scope
- if (scope && !Roles.canAddUserToRole(user._id, roleName, scope)) {
+ if (scope && !await Roles.canAddUserToRole(user._id, roleName, scope)) {
throw new Meteor.Error('error-invalid-user', 'User is not part of given room', {
method: 'authorization:addUserToRole',
});
}
- const add = Roles.addUserRoles(user._id, roleName, scope);
+ const add = await Roles.addUserRoles(user._id, [roleName], scope);
if (settings.get('UI_DisplayRoles')) {
api.broadcast('user.roleUpdate', {
diff --git a/app/authorization/server/methods/deleteRole.js b/app/authorization/server/methods/deleteRole.ts
similarity index 67%
rename from app/authorization/server/methods/deleteRole.js
rename to app/authorization/server/methods/deleteRole.ts
index 8613e1761b0a..8925942b23f3 100644
--- a/app/authorization/server/methods/deleteRole.js
+++ b/app/authorization/server/methods/deleteRole.ts
@@ -1,10 +1,10 @@
import { Meteor } from 'meteor/meteor';
-import * as Models from '../../../models/server';
+import { Roles } from '../../../models/server/raw';
import { hasPermission } from '../functions/hasPermission';
Meteor.methods({
- 'authorization:deleteRole'(roleName) {
+ async 'authorization:deleteRole'(roleName) {
if (!Meteor.userId() || !hasPermission(Meteor.userId(), 'access-permissions')) {
throw new Meteor.Error('error-action-not-allowed', 'Accessing permissions is not allowed', {
method: 'authorization:deleteRole',
@@ -12,7 +12,7 @@ Meteor.methods({
});
}
- const role = Models.Roles.findOne(roleName);
+ const role = await Roles.findOne(roleName);
if (!role) {
throw new Meteor.Error('error-invalid-role', 'Invalid role', {
method: 'authorization:deleteRole',
@@ -25,16 +25,14 @@ Meteor.methods({
});
}
- const roleScope = role.scope || 'Users';
- const model = Models[roleScope];
- const existingUsers = model && model.findUsersInRoles && model.findUsersInRoles(roleName);
+ const users = await(await Roles.findUsersInRole(roleName)).count();
- if (existingUsers && existingUsers.count() > 0) {
+ if (users > 0) {
throw new Meteor.Error('error-role-in-use', 'Cannot delete role because it\'s in use', {
method: 'authorization:deleteRole',
});
}
- return Models.Roles.remove(role.name);
+ return Roles.removeById(role.name);
},
});
diff --git a/app/authorization/server/methods/removeRoleFromPermission.js b/app/authorization/server/methods/removeRoleFromPermission.ts
similarity index 70%
rename from app/authorization/server/methods/removeRoleFromPermission.js
rename to app/authorization/server/methods/removeRoleFromPermission.ts
index e0aa20ed34db..c31592a0ceca 100644
--- a/app/authorization/server/methods/removeRoleFromPermission.js
+++ b/app/authorization/server/methods/removeRoleFromPermission.ts
@@ -1,13 +1,18 @@
import { Meteor } from 'meteor/meteor';
-import { Permissions } from '../../../models/server';
import { hasPermission } from '../functions/hasPermission';
import { CONSTANTS } from '../../lib';
+import { Permissions } from '../../../models/server/raw';
Meteor.methods({
- 'authorization:removeRoleFromPermission'(permissionId, role) {
+ async 'authorization:removeRoleFromPermission'(permissionId, role) {
const uid = Meteor.userId();
- const permission = Permissions.findOneById(permissionId);
+ const permission = await Permissions.findOneById(permissionId);
+
+
+ if (!permission) {
+ throw new Meteor.Error('error-permission-not-found', 'Permission not found', { method: 'authorization:removeRoleFromPermission' });
+ }
if (!uid || !hasPermission(uid, 'access-permissions') || (permission.level === CONSTANTS.SETTINGS_LEVEL && !hasPermission(uid, 'access-setting-permissions'))) {
throw new Meteor.Error('error-action-not-allowed', 'Removing permission is not allowed', {
diff --git a/app/authorization/server/methods/removeUserFromRole.js b/app/authorization/server/methods/removeUserFromRole.js
index 9a36a8895870..d98ff825af9b 100644
--- a/app/authorization/server/methods/removeUserFromRole.js
+++ b/app/authorization/server/methods/removeUserFromRole.js
@@ -1,13 +1,13 @@
import { Meteor } from 'meteor/meteor';
import _ from 'underscore';
-import { Roles } from '../../../models/server';
import { settings } from '../../../settings/server';
import { hasPermission } from '../functions/hasPermission';
import { api } from '../../../../server/sdk/api';
+import { Roles } from '../../../models/server/raw';
Meteor.methods({
- 'authorization:removeUserFromRole'(roleName, username, scope) {
+ async 'authorization:removeUserFromRole'(roleName, username, scope) {
if (!Meteor.userId() || !hasPermission(Meteor.userId(), 'access-permissions')) {
throw new Meteor.Error('error-action-not-allowed', 'Access permissions is not allowed', {
method: 'authorization:removeUserFromRole',
@@ -44,7 +44,7 @@ Meteor.methods({
},
}).count();
- const userIsAdmin = user.roles.indexOf('admin') > -1;
+ const userIsAdmin = user.roles?.indexOf('admin') > -1;
if (adminCount === 1 && userIsAdmin) {
throw new Meteor.Error('error-action-not-allowed', 'Leaving the app without admins is not allowed', {
method: 'removeUserFromRole',
@@ -53,7 +53,7 @@ Meteor.methods({
}
}
- const remove = Roles.removeUserRoles(user._id, roleName, scope);
+ const remove = await Roles.removeUserRoles(user._id, [roleName], scope);
if (settings.get('UI_DisplayRoles')) {
api.broadcast('user.roleUpdate', {
type: 'removed',
diff --git a/app/authorization/server/methods/saveRole.js b/app/authorization/server/methods/saveRole.ts
similarity index 80%
rename from app/authorization/server/methods/saveRole.js
rename to app/authorization/server/methods/saveRole.ts
index 5e09f211240d..04f431ba9906 100644
--- a/app/authorization/server/methods/saveRole.js
+++ b/app/authorization/server/methods/saveRole.ts
@@ -1,12 +1,12 @@
import { Meteor } from 'meteor/meteor';
-import { Roles } from '../../../models/server';
import { settings } from '../../../settings/server';
import { hasPermission } from '../functions/hasPermission';
import { api } from '../../../../server/sdk/api';
+import { Roles } from '../../../models/server/raw';
Meteor.methods({
- 'authorization:saveRole'(roleData) {
+ async 'authorization:saveRole'(roleData) {
if (!Meteor.userId() || !hasPermission(Meteor.userId(), 'access-permissions')) {
throw new Meteor.Error('error-action-not-allowed', 'Accessing permissions is not allowed', {
method: 'authorization:saveRole',
@@ -24,7 +24,7 @@ Meteor.methods({
roleData.scope = 'Users';
}
- const update = Roles.createOrUpdate(roleData.name, roleData.scope, roleData.description, false, roleData.mandatory2fa);
+ const update = await Roles.createOrUpdate(roleData.name, roleData.scope, roleData.description, false, roleData.mandatory2fa);
if (settings.get('UI_DisplayRoles')) {
api.broadcast('user.roleUpdate', {
type: 'changed',
diff --git a/app/authorization/server/streamer/permissions/index.js b/app/authorization/server/streamer/permissions/index.js
deleted file mode 100644
index edffbdfe3e73..000000000000
--- a/app/authorization/server/streamer/permissions/index.js
+++ /dev/null
@@ -1,25 +0,0 @@
-import { Meteor } from 'meteor/meteor';
-
-import Permissions from '../../../../models/server/models/Permissions';
-
-Meteor.methods({
- 'permissions/get'(updatedAt) {
- // TODO: should we return this for non logged users?
- // TODO: we could cache this collection
-
- const records = Permissions.find().fetch();
-
- if (updatedAt instanceof Date) {
- return {
- update: records.filter((record) => record._updatedAt > updatedAt),
- remove: Permissions.trashFindDeletedAfter(
- updatedAt,
- {},
- { fields: { _id: 1, _deletedAt: 1 } },
- ).fetch(),
- };
- }
-
- return records;
- },
-});
diff --git a/app/authorization/server/streamer/permissions/index.ts b/app/authorization/server/streamer/permissions/index.ts
new file mode 100644
index 000000000000..fcc3bad0e34c
--- /dev/null
+++ b/app/authorization/server/streamer/permissions/index.ts
@@ -0,0 +1,30 @@
+import { Meteor } from 'meteor/meteor';
+import { check, Match } from 'meteor/check';
+
+import { Permissions } from '../../../../models/server/raw';
+
+Meteor.methods({
+ async 'permissions/get'(updatedAt: Date) {
+ check(updatedAt, Match.Maybe(Date));
+
+ // TODO: should we return this for non logged users?
+ // TODO: we could cache this collection
+
+ const records = await Permissions.find(
+ updatedAt && { _updatedAt: { $gt: updatedAt } },
+ ).toArray();
+
+ if (updatedAt instanceof Date) {
+ return {
+ update: records,
+ remove: await Permissions.trashFindDeletedAfter(
+ updatedAt,
+ {},
+ { projection: { _id: 1, _deletedAt: 1 } },
+ ).toArray(),
+ };
+ }
+
+ return records;
+ },
+});
diff --git a/app/autotranslate/server/permissions.js b/app/autotranslate/server/permissions.js
deleted file mode 100644
index 64ce0028fa87..000000000000
--- a/app/autotranslate/server/permissions.js
+++ /dev/null
@@ -1,11 +0,0 @@
-import { Meteor } from 'meteor/meteor';
-
-import { Permissions } from '../../models';
-
-Meteor.startup(() => {
- if (Permissions) {
- if (!Permissions.findOne({ _id: 'auto-translate' })) {
- Permissions.insert({ _id: 'auto-translate', roles: ['admin'] });
- }
- }
-});
diff --git a/app/autotranslate/server/permissions.ts b/app/autotranslate/server/permissions.ts
new file mode 100644
index 000000000000..5ce05e8f1ef7
--- /dev/null
+++ b/app/autotranslate/server/permissions.ts
@@ -0,0 +1,9 @@
+import { Meteor } from 'meteor/meteor';
+
+import { Permissions } from '../../models/server/raw';
+
+Meteor.startup(async () => {
+ if (!await Permissions.findOne({ _id: 'auto-translate' })) {
+ Permissions.create('auto-translate', ['admin']);
+ }
+});
diff --git a/app/cas/server/cas_server.js b/app/cas/server/cas_server.js
index 646e87a8f053..cc569eeab441 100644
--- a/app/cas/server/cas_server.js
+++ b/app/cas/server/cas_server.js
@@ -10,7 +10,8 @@ import CAS from 'cas';
import { logger } from './cas_rocketchat';
import { settings } from '../../settings';
-import { Rooms, CredentialTokens } from '../../models/server';
+import { Rooms } from '../../models/server';
+import { CredentialTokens } from '../../models/server/raw';
import { _setRealName } from '../../lib';
import { createRoom } from '../../lib/server/functions/createRoom';
@@ -43,7 +44,7 @@ const casTicket = function(req, token, callback) {
service: `${ appUrl }/_cas/${ token }`,
});
- cas.validate(ticketId, Meteor.bindEnvironment(function(err, status, username, details) {
+ cas.validate(ticketId, Meteor.bindEnvironment(async function(err, status, username, details) {
if (err) {
logger.error(`error when trying to validate: ${ err.message }`);
} else if (status) {
@@ -54,11 +55,11 @@ const casTicket = function(req, token, callback) {
if (details && details.attributes) {
_.extend(user_info, { attributes: details.attributes });
}
- CredentialTokens.create(token, user_info);
+ await CredentialTokens.create(token, user_info);
} else {
logger.error(`Unable to validate ticket: ${ ticketId }`);
}
- // logger.debug("Receveied response: " + JSON.stringify(details, null , 4));
+ // logger.debug("Received response: " + JSON.stringify(details, null , 4));
callback();
}));
@@ -114,7 +115,8 @@ Accounts.registerLoginHandler(function(options) {
return undefined;
}
- const credentials = CredentialTokens.findOneById(options.cas.credentialToken);
+ // TODO: Sync wrapper due to the chain conversion to async models
+ const credentials = Promise.await(CredentialTokens.findOneNotExpiredById(options.cas.credentialToken));
if (credentials === undefined) {
throw new Meteor.Error(Accounts.LoginCancelledError.numericError,
'no matching login attempt found');
diff --git a/app/channel-settings/server/functions/saveRoomName.js b/app/channel-settings/server/functions/saveRoomName.js
index 5d3197d133b3..0cc31cfa77b4 100644
--- a/app/channel-settings/server/functions/saveRoomName.js
+++ b/app/channel-settings/server/functions/saveRoomName.js
@@ -1,6 +1,7 @@
import { Meteor } from 'meteor/meteor';
-import { Rooms, Messages, Subscriptions, Integrations } from '../../../models/server';
+import { Rooms, Messages, Subscriptions } from '../../../models/server';
+import { Integrations } from '../../../models/server/raw';
import { roomTypes, getValidRoomName } from '../../../utils/server';
import { callbacks } from '../../../callbacks/server';
import { checkUsernameAvailability } from '../../../lib/server/functions';
@@ -19,7 +20,7 @@ const updateRoomName = (rid, displayName, isDiscussion) => {
return Rooms.setNameById(rid, slugifiedRoomName, displayName) && Subscriptions.updateNameAndAlertByRoomId(rid, slugifiedRoomName, displayName);
};
-export const saveRoomName = function(rid, displayName, user, sendMessage = true) {
+export async function saveRoomName(rid, displayName, user, sendMessage = true) {
const room = Rooms.findOneById(rid);
if (roomTypes.getConfig(room.t).preventRenaming()) {
throw new Meteor.Error('error-not-allowed', 'Not allowed', {
@@ -35,10 +36,10 @@ export const saveRoomName = function(rid, displayName, user, sendMessage = true)
return;
}
- Integrations.updateRoomName(room.name, displayName);
+ await Integrations.updateRoomName(room.name, displayName);
if (sendMessage) {
Messages.createRoomRenamedWithRoomIdRoomNameAndUser(rid, displayName, user);
}
callbacks.run('afterRoomNameChange', { rid, name: displayName, oldName: room.name });
return displayName;
-};
+}
diff --git a/app/channel-settings/server/methods/saveRoomSettings.js b/app/channel-settings/server/methods/saveRoomSettings.js
index 811c492fb70d..59c0bb239f79 100644
--- a/app/channel-settings/server/methods/saveRoomSettings.js
+++ b/app/channel-settings/server/methods/saveRoomSettings.js
@@ -128,7 +128,7 @@ const validators = {
const settingSavers = {
roomName({ value, rid, user, room }) {
- if (!saveRoomName(rid, value, user)) {
+ if (!Promise.await(saveRoomName(rid, value, user))) {
return;
}
@@ -231,13 +231,13 @@ const settingSavers = {
favorite({ value, rid }) {
Rooms.saveFavoriteById(rid, value.favorite, value.defaultValue);
},
- roomAvatar({ value, rid, user }) {
- setRoomAvatar(rid, value, user);
+ async roomAvatar({ value, rid, user }) {
+ await setRoomAvatar(rid, value, user);
},
};
Meteor.methods({
- saveRoomSettings(rid, settings, value) {
+ async saveRoomSettings(rid, settings, value) {
const userId = Meteor.userId();
if (!userId) {
@@ -313,10 +313,10 @@ Meteor.methods({
});
// saving data
- Object.keys(settings).forEach((setting) => {
+ for await (const setting of Object.keys(settings)) {
const value = settings[setting];
- const saver = settingSavers[setting];
+ const saver = await settingSavers[setting];
if (saver) {
saver({
value,
@@ -325,7 +325,7 @@ Meteor.methods({
user,
});
}
- });
+ }
Meteor.defer(function() {
const room = Rooms.findOneById(rid);
diff --git a/app/cloud/server/functions/buildRegistrationData.js b/app/cloud/server/functions/buildRegistrationData.js
index d8ecff67687f..5346558e23ab 100644
--- a/app/cloud/server/functions/buildRegistrationData.js
+++ b/app/cloud/server/functions/buildRegistrationData.js
@@ -1,10 +1,11 @@
import { settings } from '../../../settings/server';
-import { Users, Statistics } from '../../../models/server';
+import { Users } from '../../../models/server';
+import { Statistics } from '../../../models/server/raw';
import { statistics } from '../../../statistics';
import { LICENSE_VERSION } from '../license';
-export function buildWorkspaceRegistrationData() {
- const stats = Statistics.findLast() || statistics.get();
+export async function buildWorkspaceRegistrationData() {
+ const stats = await Statistics.findLast() || statistics.get();
const address = settings.get('Site_Url');
const siteName = settings.get('Site_Name');
diff --git a/app/cloud/server/functions/startRegisterWorkspace.js b/app/cloud/server/functions/startRegisterWorkspace.js
index bb533c79c580..2f9e4f90b789 100644
--- a/app/cloud/server/functions/startRegisterWorkspace.js
+++ b/app/cloud/server/functions/startRegisterWorkspace.js
@@ -7,18 +7,17 @@ import { Settings } from '../../../models';
import { buildWorkspaceRegistrationData } from './buildRegistrationData';
import { SystemLogger } from '../../../../server/lib/logger/system';
-
-export function startRegisterWorkspace(resend = false) {
+export async function startRegisterWorkspace(resend = false) {
const { workspaceRegistered, connectToCloud } = retrieveRegistrationStatus();
if ((workspaceRegistered && connectToCloud) || process.env.TEST_MODE) {
- syncWorkspace(true);
+ await syncWorkspace(true);
return true;
}
Settings.updateValueById('Register_Server', true);
- const regInfo = buildWorkspaceRegistrationData();
+ const regInfo = await buildWorkspaceRegistrationData();
const cloudUrl = settings.get('Cloud_Url');
diff --git a/app/cloud/server/functions/syncWorkspace.js b/app/cloud/server/functions/syncWorkspace.js
index 03f67acf4a4b..1b1021402e25 100644
--- a/app/cloud/server/functions/syncWorkspace.js
+++ b/app/cloud/server/functions/syncWorkspace.js
@@ -10,13 +10,13 @@ import { getAndCreateNpsSurvey } from '../../../../server/services/nps/getAndCre
import { NPS, Banner } from '../../../../server/sdk';
import { SystemLogger } from '../../../../server/lib/logger/system';
-export function syncWorkspace(reconnectCheck = false) {
+export async function syncWorkspace(reconnectCheck = false) {
const { workspaceRegistered, connectToCloud } = retrieveRegistrationStatus();
if (!workspaceRegistered || (!connectToCloud && !reconnectCheck)) {
return false;
}
- const info = buildWorkspaceRegistrationData();
+ const info = await buildWorkspaceRegistrationData();
const workspaceUrl = settings.get('Cloud_Workspace_Registration_Client_Uri');
@@ -64,11 +64,11 @@ export function syncWorkspace(reconnectCheck = false) {
const startAt = new Date(data.nps.startAt);
- Promise.await(NPS.create({
+ await NPS.create({
npsId,
startAt,
expireAt: new Date(expireAt),
- }));
+ });
const now = new Date();
@@ -79,19 +79,19 @@ export function syncWorkspace(reconnectCheck = false) {
// add banners
if (data.banners) {
- for (const banner of data.banners) {
+ for await (const banner of data.banners) {
const {
createdAt,
expireAt,
startAt,
} = banner;
- Promise.await(Banner.create({
+ await Banner.create({
...banner,
createdAt: new Date(createdAt),
expireAt: new Date(expireAt),
startAt: new Date(startAt),
- }));
+ });
}
}
diff --git a/app/cloud/server/index.js b/app/cloud/server/index.js
index 98ae3b710b96..eb239b095c68 100644
--- a/app/cloud/server/index.js
+++ b/app/cloud/server/index.js
@@ -6,9 +6,12 @@ import { getWorkspaceAccessToken } from './functions/getWorkspaceAccessToken';
import { getWorkspaceAccessTokenWithScope } from './functions/getWorkspaceAccessTokenWithScope';
import { getWorkspaceLicense } from './functions/getWorkspaceLicense';
import { getUserCloudAccessToken } from './functions/getUserCloudAccessToken';
+import { retrieveRegistrationStatus } from './functions/retrieveRegistrationStatus';
import { getWorkspaceKey } from './functions/getWorkspaceKey';
import { syncWorkspace } from './functions/syncWorkspace';
+import { connectWorkspace } from './functions/connectWorkspace';
import { settings } from '../../settings/server';
+import { SystemLogger } from '../../../server/lib/logger/system';
const licenseCronName = 'Cloud Workspace Sync';
@@ -34,6 +37,22 @@ Meteor.startup(function() {
job: syncWorkspace,
});
});
+
+ const { workspaceRegistered } = retrieveRegistrationStatus();
+
+ if (process.env.REG_TOKEN && process.env.REG_TOKEN !== '' && !workspaceRegistered) {
+ try {
+ SystemLogger.info('REG_TOKEN Provided. Attempting to register');
+
+ if (!connectWorkspace(process.env.REG_TOKEN)) {
+ throw new Error('Couldn\'t register with token. Please make sure token is valid or hasn\'t already been used');
+ }
+
+ console.log('Successfully registered with token provided by REG_TOKEN!');
+ } catch (e) {
+ SystemLogger.error('An error occured registering with token.', e.message);
+ }
+ }
});
export { getWorkspaceAccessToken, getWorkspaceAccessTokenWithScope, getWorkspaceLicense, getWorkspaceKey, getUserCloudAccessToken };
diff --git a/app/cloud/server/methods.js b/app/cloud/server/methods.js
index 7723566601f5..83847711a603 100644
--- a/app/cloud/server/methods.js
+++ b/app/cloud/server/methods.js
@@ -26,7 +26,7 @@ Meteor.methods({
return retrieveRegistrationStatus();
},
- 'cloud:getWorkspaceRegisterData'() {
+ async 'cloud:getWorkspaceRegisterData'() {
if (!Meteor.userId()) {
throw new Meteor.Error('error-invalid-user', 'Invalid user', { method: 'cloud:getWorkspaceRegisterData' });
}
@@ -35,9 +35,9 @@ Meteor.methods({
throw new Meteor.Error('error-not-authorized', 'Not authorized', { method: 'cloud:getWorkspaceRegisterData' });
}
- return Buffer.from(JSON.stringify(buildWorkspaceRegistrationData())).toString('base64');
+ return Buffer.from(JSON.stringify(await buildWorkspaceRegistrationData())).toString('base64');
},
- 'cloud:registerWorkspace'() {
+ async 'cloud:registerWorkspace'() {
if (!Meteor.userId()) {
throw new Meteor.Error('error-invalid-user', 'Invalid user', { method: 'cloud:startRegister' });
}
@@ -48,7 +48,7 @@ Meteor.methods({
return startRegisterWorkspace();
},
- 'cloud:syncWorkspace'() {
+ async 'cloud:syncWorkspace'() {
if (!Meteor.userId()) {
throw new Meteor.Error('error-invalid-user', 'Invalid user', { method: 'cloud:syncWorkspace' });
}
diff --git a/app/crowd/server/crowd.js b/app/crowd/server/crowd.js
index d5b4b5b97ef3..dba130ef489e 100644
--- a/app/crowd/server/crowd.js
+++ b/app/crowd/server/crowd.js
@@ -208,7 +208,7 @@ export class CROWD {
if (settings.get('CROWD_Remove_Orphaned_Users') === true) {
logger.info('Removing user:', crowd_username);
Meteor.defer(function() {
- deleteUser(user._id);
+ Promise.await(deleteUser(user._id));
logger.info('User removed:', crowd_username);
});
}
diff --git a/app/custom-oauth/server/custom_oauth_server.js b/app/custom-oauth/server/custom_oauth_server.js
index 2be67a092547..2e62be9647eb 100644
--- a/app/custom-oauth/server/custom_oauth_server.js
+++ b/app/custom-oauth/server/custom_oauth_server.js
@@ -334,6 +334,8 @@ export class CustomOAuth {
return;
}
+ callbacks.run('afterProcessOAuthUser', { serviceName, serviceData, user });
+
// User already created or merged and has identical name as before
if (user.services && user.services[serviceName] && user.services[serviceName].id === serviceData.id && user.name === serviceData.name) {
return;
@@ -343,8 +345,6 @@ export class CustomOAuth {
throw new Meteor.Error('CustomOAuth', `User with username ${ user.username } already exists`);
}
- callbacks.run('afterProcessOAuthUser', { serviceName, serviceData, user });
-
const serviceIdKey = `services.${ serviceName }.id`;
const update = {
$set: {
diff --git a/app/custom-oauth/server/transform_helpers.tests.js b/app/custom-oauth/server/transform_helpers.tests.js
index ec1475780e68..5139edb2410e 100644
--- a/app/custom-oauth/server/transform_helpers.tests.js
+++ b/app/custom-oauth/server/transform_helpers.tests.js
@@ -1,5 +1,3 @@
-/* eslint-env mocha */
-
import { expect } from 'chai';
import {
diff --git a/app/custom-sounds/server/methods/deleteCustomSound.js b/app/custom-sounds/server/methods/deleteCustomSound.js
index b72c852bacfc..d168fac12d1d 100644
--- a/app/custom-sounds/server/methods/deleteCustomSound.js
+++ b/app/custom-sounds/server/methods/deleteCustomSound.js
@@ -1,16 +1,16 @@
import { Meteor } from 'meteor/meteor';
-import { CustomSounds } from '../../../models';
+import { CustomSounds } from '../../../models/server/raw';
import { hasPermission } from '../../../authorization';
import { Notifications } from '../../../notifications';
import { RocketChatFileCustomSoundsInstance } from '../startup/custom-sounds';
Meteor.methods({
- deleteCustomSound(_id) {
+ async deleteCustomSound(_id) {
let sound = null;
if (hasPermission(this.userId, 'manage-sounds')) {
- sound = CustomSounds.findOneById(_id);
+ sound = await CustomSounds.findOneById(_id);
} else {
throw new Meteor.Error('not_authorized');
}
@@ -20,7 +20,7 @@ Meteor.methods({
}
RocketChatFileCustomSoundsInstance.deleteFile(`${ sound._id }.${ sound.extension }`);
- CustomSounds.removeById(_id);
+ await CustomSounds.removeById(_id);
Notifications.notifyAll('deleteCustomSound', { soundData: sound });
return true;
diff --git a/app/custom-sounds/server/methods/insertOrUpdateSound.js b/app/custom-sounds/server/methods/insertOrUpdateSound.js
index d3fe25e0173b..b1fa7c749747 100644
--- a/app/custom-sounds/server/methods/insertOrUpdateSound.js
+++ b/app/custom-sounds/server/methods/insertOrUpdateSound.js
@@ -3,12 +3,12 @@ import s from 'underscore.string';
import { check } from 'meteor/check';
import { hasPermission } from '../../../authorization';
-import { CustomSounds } from '../../../models';
+import { CustomSounds } from '../../../models/server/raw';
import { Notifications } from '../../../notifications';
import { RocketChatFileCustomSoundsInstance } from '../startup/custom-sounds';
Meteor.methods({
- insertOrUpdateSound(soundData) {
+ async insertOrUpdateSound(soundData) {
if (!hasPermission(this.userId, 'manage-sounds')) {
throw new Meteor.Error('not_authorized');
}
@@ -34,9 +34,9 @@ Meteor.methods({
if (soundData._id) {
check(soundData._id, String);
- matchingResults = CustomSounds.findByNameExceptId(soundData.name, soundData._id).fetch();
+ matchingResults = await CustomSounds.findByNameExceptId(soundData.name, soundData._id).toArray();
} else {
- matchingResults = CustomSounds.findByName(soundData.name).fetch();
+ matchingResults = await CustomSounds.findByName(soundData.name).toArray();
}
if (matchingResults.length > 0) {
@@ -50,7 +50,7 @@ Meteor.methods({
extension: soundData.extension,
};
- const _id = CustomSounds.create(createSound);
+ const _id = await (await CustomSounds.create(createSound)).insertedId;
createSound._id = _id;
return _id;
@@ -61,7 +61,7 @@ Meteor.methods({
}
if (soundData.name !== soundData.previousName) {
- CustomSounds.setName(soundData._id, soundData.name);
+ await CustomSounds.setName(soundData._id, soundData.name);
Notifications.notifyAll('updateCustomSound', { soundData });
}
diff --git a/app/custom-sounds/server/methods/listCustomSounds.js b/app/custom-sounds/server/methods/listCustomSounds.js
index 90bf6db20435..475da52286be 100644
--- a/app/custom-sounds/server/methods/listCustomSounds.js
+++ b/app/custom-sounds/server/methods/listCustomSounds.js
@@ -1,9 +1,9 @@
import { Meteor } from 'meteor/meteor';
-import { CustomSounds } from '../../../models';
+import { CustomSounds } from '../../../models/server/raw';
Meteor.methods({
- listCustomSounds() {
- return CustomSounds.find({}).fetch();
+ async listCustomSounds() {
+ return CustomSounds.find({}).toArray();
},
});
diff --git a/app/discussion/client/createDiscussionMessageAction.js b/app/discussion/client/createDiscussionMessageAction.js
index 7d107be268b2..120001c9e52d 100644
--- a/app/discussion/client/createDiscussionMessageAction.js
+++ b/app/discussion/client/createDiscussionMessageAction.js
@@ -22,12 +22,12 @@ Meteor.startup(function() {
label: 'Discussion_start',
context: ['message', 'message-mobile'],
async action() {
- const { msg: message } = messageArgs(this);
+ const { msg: message, room } = messageArgs(this);
imperativeModal.open({
component: CreateDiscussion,
props: {
- defaultParentRoom: message.rid,
+ defaultParentRoom: room.prid || room._id,
onClose: imperativeModal.close,
parentMessageId: message._id,
nameSuggestion: message?.msg?.substr(0, 140),
diff --git a/app/discussion/client/discussionFromMessageBox.js b/app/discussion/client/discussionFromMessageBox.js
index 668cf6ac75b5..e2a8b29c845b 100644
--- a/app/discussion/client/discussionFromMessageBox.js
+++ b/app/discussion/client/discussionFromMessageBox.js
@@ -20,7 +20,7 @@ Meteor.startup(function() {
imperativeModal.open({
component: CreateDiscussion,
props: {
- defaultParentRoom: data.rid,
+ defaultParentRoom: data.prid || data.rid,
onClose: imperativeModal.close,
},
});
diff --git a/app/discussion/server/methods/createDiscussion.js b/app/discussion/server/methods/createDiscussion.js
index b9d28a0d58df..8c1ea7dbb3d3 100644
--- a/app/discussion/server/methods/createDiscussion.js
+++ b/app/discussion/server/methods/createDiscussion.js
@@ -38,7 +38,7 @@ const mentionMessage = (rid, { _id, username, name }, message_embedded) => {
};
const create = ({ prid, pmid, t_name, reply, users, user, encrypted }) => {
- // if you set both, prid and pmid, and the rooms doesnt match... should throw an error)
+ // if you set both, prid and pmid, and the rooms dont match... should throw an error)
let message = false;
if (pmid) {
message = Messages.findOne({ _id: pmid });
diff --git a/app/discussion/server/permissions.js b/app/discussion/server/permissions.ts
similarity index 87%
rename from app/discussion/server/permissions.js
rename to app/discussion/server/permissions.ts
index 3d54e4c66b16..da3ac2ee2290 100644
--- a/app/discussion/server/permissions.js
+++ b/app/discussion/server/permissions.ts
@@ -1,6 +1,7 @@
import { Meteor } from 'meteor/meteor';
-import { Permissions } from '../../models';
+import { Permissions } from '../../models/server/raw';
+
Meteor.startup(() => {
// Add permissions for discussion
diff --git a/app/e2e/server/beforeCreateRoom.js b/app/e2e/server/beforeCreateRoom.js
index ce3b21ad6935..a8c6a8933519 100644
--- a/app/e2e/server/beforeCreateRoom.js
+++ b/app/e2e/server/beforeCreateRoom.js
@@ -3,9 +3,8 @@ import { settings } from '../../settings/server';
callbacks.add('beforeCreateRoom', ({ type, extraData }) => {
if (
- settings.get('E2E_Enabled') && ((type === 'd' && settings.get('E2E_Enabled_Default_DirectRooms'))
- || (type === 'p' && settings.get('E2E_Enabled_Default_PrivateRooms')))
- ) {
+ settings.get('E2E_Enable') && ((type === 'd' && settings.get('E2E_Enabled_Default_DirectRooms'))
+ || (type === 'p' && settings.get('E2E_Enabled_Default_PrivateRooms')))) {
extraData.encrypted = extraData.encrypted ?? true;
}
});
diff --git a/app/e2e/server/settings.ts b/app/e2e/server/settings.ts
index 3b3aad9e6dc7..20c624fed9b5 100644
--- a/app/e2e/server/settings.ts
+++ b/app/e2e/server/settings.ts
@@ -11,11 +11,13 @@ settingsRegistry.addGroup('E2E Encryption', function() {
this.add('E2E_Enabled_Default_DirectRooms', false, {
type: 'boolean',
+ public: true,
enableQuery: { _id: 'E2E_Enable', value: true },
});
this.add('E2E_Enabled_Default_PrivateRooms', false, {
type: 'boolean',
+ public: true,
enableQuery: { _id: 'E2E_Enable', value: true },
});
});
diff --git a/app/emoji-custom/server/methods/deleteEmojiCustom.js b/app/emoji-custom/server/methods/deleteEmojiCustom.js
index 7393f245b459..2964c5ff6cd6 100644
--- a/app/emoji-custom/server/methods/deleteEmojiCustom.js
+++ b/app/emoji-custom/server/methods/deleteEmojiCustom.js
@@ -2,22 +2,22 @@ import { Meteor } from 'meteor/meteor';
import { api } from '../../../../server/sdk/api';
import { hasPermission } from '../../../authorization';
-import { EmojiCustom } from '../../../models';
+import { EmojiCustom } from '../../../models/server/raw';
import { RocketChatFileEmojiCustomInstance } from '../startup/emoji-custom';
Meteor.methods({
- deleteEmojiCustom(emojiID) {
+ async deleteEmojiCustom(emojiID) {
if (!hasPermission(this.userId, 'manage-emoji')) {
throw new Meteor.Error('not_authorized');
}
- const emoji = EmojiCustom.findOneById(emojiID);
+ const emoji = await EmojiCustom.findOneById(emojiID);
if (emoji == null) {
throw new Meteor.Error('Custom_Emoji_Error_Invalid_Emoji', 'Invalid emoji', { method: 'deleteEmojiCustom' });
}
RocketChatFileEmojiCustomInstance.deleteFile(encodeURIComponent(`${ emoji.name }.${ emoji.extension }`));
- EmojiCustom.removeById(emojiID);
+ await EmojiCustom.removeById(emojiID);
api.broadcast('emoji.deleteCustom', emoji);
return true;
diff --git a/app/emoji-custom/server/methods/insertOrUpdateEmoji.js b/app/emoji-custom/server/methods/insertOrUpdateEmoji.js
index b96b40b2fbd0..23843c81cec9 100644
--- a/app/emoji-custom/server/methods/insertOrUpdateEmoji.js
+++ b/app/emoji-custom/server/methods/insertOrUpdateEmoji.js
@@ -4,12 +4,12 @@ import s from 'underscore.string';
import limax from 'limax';
import { hasPermission } from '../../../authorization';
-import { EmojiCustom } from '../../../models';
+import { EmojiCustom } from '../../../models/server/raw';
import { RocketChatFileEmojiCustomInstance } from '../startup/emoji-custom';
import { api } from '../../../../server/sdk/api';
Meteor.methods({
- insertOrUpdateEmoji(emojiData) {
+ async insertOrUpdateEmoji(emojiData) {
if (!hasPermission(this.userId, 'manage-emoji')) {
throw new Meteor.Error('not_authorized');
}
@@ -50,14 +50,14 @@ Meteor.methods({
let matchingResults = [];
if (emojiData._id) {
- matchingResults = EmojiCustom.findByNameOrAliasExceptID(emojiData.name, emojiData._id).fetch();
- for (const alias of emojiData.aliases) {
- matchingResults = matchingResults.concat(EmojiCustom.findByNameOrAliasExceptID(alias, emojiData._id).fetch());
+ matchingResults = await EmojiCustom.findByNameOrAliasExceptID(emojiData.name, emojiData._id).toArray();
+ for await (const alias of emojiData.aliases) {
+ matchingResults = matchingResults.concat(await EmojiCustom.findByNameOrAliasExceptID(alias, emojiData._id).toArray());
}
} else {
- matchingResults = EmojiCustom.findByNameOrAlias(emojiData.name).fetch();
- for (const alias of emojiData.aliases) {
- matchingResults = matchingResults.concat(EmojiCustom.findByNameOrAlias(alias).fetch());
+ matchingResults = await EmojiCustom.findByNameOrAlias(emojiData.name).toArray();
+ for await (const alias of emojiData.aliases) {
+ matchingResults = matchingResults.concat(await EmojiCustom.findByNameOrAlias(alias).toArray());
}
}
@@ -77,7 +77,7 @@ Meteor.methods({
extension: emojiData.extension,
};
- const _id = EmojiCustom.create(createEmoji);
+ const _id = (await EmojiCustom.create(createEmoji)).insertedId;
api.broadcast('emoji.updateCustom', createEmoji);
@@ -90,7 +90,7 @@ Meteor.methods({
RocketChatFileEmojiCustomInstance.deleteFile(encodeURIComponent(`${ emojiData.previousName }.${ emojiData.extension }`));
RocketChatFileEmojiCustomInstance.deleteFile(encodeURIComponent(`${ emojiData.previousName }.${ emojiData.previousExtension }`));
- EmojiCustom.setExtension(emojiData._id, emojiData.extension);
+ await EmojiCustom.setExtension(emojiData._id, emojiData.extension);
} else if (emojiData.name !== emojiData.previousName) {
const rs = RocketChatFileEmojiCustomInstance.getFileWithReadStream(encodeURIComponent(`${ emojiData.previousName }.${ emojiData.previousExtension }`));
if (rs !== null) {
@@ -104,13 +104,13 @@ Meteor.methods({
}
if (emojiData.name !== emojiData.previousName) {
- EmojiCustom.setName(emojiData._id, emojiData.name);
+ await EmojiCustom.setName(emojiData._id, emojiData.name);
}
if (emojiData.aliases) {
- EmojiCustom.setAliases(emojiData._id, emojiData.aliases);
+ await EmojiCustom.setAliases(emojiData._id, emojiData.aliases);
} else {
- EmojiCustom.setAliases(emojiData._id, []);
+ await EmojiCustom.setAliases(emojiData._id, []);
}
api.broadcast('emoji.updateCustom', emojiData);
diff --git a/app/emoji-custom/server/methods/listEmojiCustom.js b/app/emoji-custom/server/methods/listEmojiCustom.js
index d06b382af85e..d66aeee1a6ad 100644
--- a/app/emoji-custom/server/methods/listEmojiCustom.js
+++ b/app/emoji-custom/server/methods/listEmojiCustom.js
@@ -1,9 +1,9 @@
import { Meteor } from 'meteor/meteor';
-import { EmojiCustom } from '../../../models';
+import { EmojiCustom } from '../../../models/server/raw';
Meteor.methods({
- listEmojiCustom(options = {}) {
- return EmojiCustom.find(options).fetch();
+ async listEmojiCustom(options = {}) {
+ return EmojiCustom.find(options).toArray();
},
});
diff --git a/app/federation/server/endpoints/dispatch.js b/app/federation/server/endpoints/dispatch.js
index ae392ac8aac8..333a30bbeebf 100644
--- a/app/federation/server/endpoints/dispatch.js
+++ b/app/federation/server/endpoints/dispatch.js
@@ -4,12 +4,13 @@ import { API } from '../../../api/server';
import { serverLogger } from '../lib/logger';
import { contextDefinitions, eventTypes } from '../../../models/server/models/FederationEvents';
import {
- FederationRoomEvents, FederationServers,
+ FederationRoomEvents,
Messages,
Rooms,
Subscriptions,
Users,
} from '../../../models/server';
+import { FederationServers } from '../../../models/server/raw';
import { normalizers } from '../normalizers';
import { deleteRoom } from '../../../lib/server/functions';
import { Notifications } from '../../../notifications/server';
@@ -139,7 +140,7 @@ const eventHandlers = {
// Refresh the servers list
if (federationAltered) {
- FederationServers.refreshServers();
+ await FederationServers.refreshServers();
// Update the room's federation property
Rooms.update({ _id: roomId }, { $set: { 'federation.domains': domainsAfterAdd } });
@@ -163,7 +164,7 @@ const eventHandlers = {
Subscriptions.removeByRoomIdAndUserId(roomId, user._id);
// Refresh the servers list
- FederationServers.refreshServers();
+ await FederationServers.refreshServers();
// Update the room's federation property
Rooms.update({ _id: roomId }, { $set: { 'federation.domains': domainsAfterRemoval } });
@@ -186,7 +187,7 @@ const eventHandlers = {
Subscriptions.removeByRoomIdAndUserId(roomId, user._id);
// Refresh the servers list
- FederationServers.refreshServers();
+ await FederationServers.refreshServers();
// Update the room's federation property
Rooms.update({ _id: roomId }, { $set: { 'federation.domains': domainsAfterRemoval } });
@@ -226,7 +227,7 @@ const eventHandlers = {
const { federation: { origin } } = denormalizedMessage;
- const { upload, buffer } = getUpload(origin, denormalizedMessage.file._id);
+ const { upload, buffer } = await getUpload(origin, denormalizedMessage.file._id);
const oldUploadId = upload._id;
@@ -444,7 +445,7 @@ const eventHandlers = {
};
API.v1.addRoute('federation.events.dispatch', { authRequired: false, rateLimiterOptions: { numRequestsAllowed: 30, intervalTimeInMS: 1000 } }, {
- async post() {
+ post() {
if (!isFederationEnabled()) {
return API.v1.failure('Federation not enabled');
}
@@ -454,7 +455,7 @@ API.v1.addRoute('federation.events.dispatch', { authRequired: false, rateLimiter
let payload;
try {
- payload = decryptIfNeeded(this.request, this.bodyParams);
+ payload = Promise.await(decryptIfNeeded(this.request, this.bodyParams));
} catch (err) {
return API.v1.failure('Could not decrypt payload');
}
@@ -472,7 +473,7 @@ API.v1.addRoute('federation.events.dispatch', { authRequired: false, rateLimiter
let eventResult;
if (eventHandlers[event.type]) {
- eventResult = await eventHandlers[event.type](event);
+ eventResult = Promise.await(eventHandlers[event.type](event));
}
// If there was an error handling the event, take action
@@ -480,7 +481,7 @@ API.v1.addRoute('federation.events.dispatch', { authRequired: false, rateLimiter
try {
serverLogger.debug({ msg: 'federation.events.dispatch => Event has missing parents', event });
- requestEventsFromLatest(event.origin, getFederationDomain(), contextDefinitions.defineType(event), event.context, eventResult.latestEventIds);
+ Promise.await(requestEventsFromLatest(event.origin, getFederationDomain(), contextDefinitions.defineType(event), event.context, eventResult.latestEventIds));
// And stop handling the events
break;
diff --git a/app/federation/server/endpoints/requestFromLatest.js b/app/federation/server/endpoints/requestFromLatest.js
index cac0168c8c12..84fd69f88d3a 100644
--- a/app/federation/server/endpoints/requestFromLatest.js
+++ b/app/federation/server/endpoints/requestFromLatest.js
@@ -8,7 +8,7 @@ import { isFederationEnabled } from '../lib/isFederationEnabled';
import { dispatchEvents } from '../handler';
API.v1.addRoute('federation.events.requestFromLatest', { authRequired: false }, {
- async post() {
+ post() {
if (!isFederationEnabled()) {
return API.v1.failure('Federation not enabled');
}
@@ -18,7 +18,7 @@ API.v1.addRoute('federation.events.requestFromLatest', { authRequired: false },
let payload;
try {
- payload = decryptIfNeeded(this.request, this.bodyParams);
+ payload = Promise.await(decryptIfNeeded(this.request, this.bodyParams));
} catch (err) {
return API.v1.failure('Could not decrypt payload');
}
@@ -54,7 +54,7 @@ API.v1.addRoute('federation.events.requestFromLatest', { authRequired: false },
}
// Dispatch all the events, on the same request
- dispatchEvents([fromDomain], missingEvents);
+ Promise.await(dispatchEvents([fromDomain], missingEvents));
return API.v1.success();
},
diff --git a/app/federation/server/endpoints/uploads.js b/app/federation/server/endpoints/uploads.js
index 7735a630f15e..a997b2aff307 100644
--- a/app/federation/server/endpoints/uploads.js
+++ b/app/federation/server/endpoints/uploads.js
@@ -1,5 +1,5 @@
import { API } from '../../../api/server';
-import { Uploads } from '../../../models/server';
+import { Uploads } from '../../../models/server/raw';
import { FileUpload } from '../../../file-upload/server';
import { isFederationEnabled } from '../lib/isFederationEnabled';
@@ -11,7 +11,7 @@ API.v1.addRoute('federation.uploads', { authRequired: false }, {
const { upload_id } = this.requestParams();
- const upload = Uploads.findOneById(upload_id);
+ const upload = Promise.await(Uploads.findOneById(upload_id));
if (!upload) {
return API.v1.failure('There is no such file in this server');
diff --git a/app/federation/server/functions/addUser.js b/app/federation/server/functions/addUser.js
index eebd1656260b..314b7893fbc1 100644
--- a/app/federation/server/functions/addUser.js
+++ b/app/federation/server/functions/addUser.js
@@ -1,15 +1,16 @@
import { Meteor } from 'meteor/meteor';
import * as federationErrors from './errors';
-import { FederationServers, Users } from '../../../models/server';
+import { Users } from '../../../models/server';
+import { FederationServers } from '../../../models/server/raw';
import { getUserByUsername } from '../handler';
-export function addUser(query) {
+export async function addUser(query) {
if (!Meteor.userId()) {
throw new Meteor.Error('error-invalid-user', 'Invalid user', { method: 'addUser' });
}
- const user = getUserByUsername(query);
+ const user = await getUserByUsername(query);
if (!user) {
throw federationErrors.userNotFound(query);
@@ -22,7 +23,7 @@ export function addUser(query) {
userId = Users.create(user);
// Refresh the servers list
- FederationServers.refreshServers();
+ await FederationServers.refreshServers();
} catch (err) {
// This might get called twice by the createDirectMessage method
// so we need to handle the situation accordingly
diff --git a/app/federation/server/functions/dashboard.js b/app/federation/server/functions/dashboard.js
index 137ef802c5dc..3f256bf3d88a 100644
--- a/app/federation/server/functions/dashboard.js
+++ b/app/federation/server/functions/dashboard.js
@@ -1,21 +1,22 @@
import { Meteor } from 'meteor/meteor';
-import { FederationServers, FederationRoomEvents, Users } from '../../../models/server';
+import { FederationRoomEvents, Users } from '../../../models/server';
+import { FederationServers } from '../../../models/server/raw';
-export function getStatistics() {
+export async function getStatistics() {
const numberOfEvents = FederationRoomEvents.find().count();
const numberOfFederatedUsers = Users.findRemote().count();
- const numberOfServers = FederationServers.find().count();
+ const numberOfServers = await FederationServers.find().count();
return { numberOfEvents, numberOfFederatedUsers, numberOfServers };
}
-export function federationGetOverviewData() {
+export async function federationGetOverviewData() {
if (!Meteor.userId()) {
throw new Meteor.Error('not-authorized');
}
- const { numberOfEvents, numberOfFederatedUsers, numberOfServers } = getStatistics();
+ const { numberOfEvents, numberOfFederatedUsers, numberOfServers } = await getStatistics();
return {
data: [{
@@ -31,12 +32,12 @@ export function federationGetOverviewData() {
};
}
-export function federationGetServers() {
+export async function federationGetServers() {
if (!Meteor.userId()) {
throw new Meteor.Error('not-authorized');
}
- const servers = FederationServers.find().fetch();
+ const servers = await FederationServers.find().toArray();
return {
data: servers,
diff --git a/app/federation/server/functions/helpers.js b/app/federation/server/functions/helpers.js
deleted file mode 100644
index 4113b6014edd..000000000000
--- a/app/federation/server/functions/helpers.js
+++ /dev/null
@@ -1,69 +0,0 @@
-import { Settings, Subscriptions, Users } from '../../../models/server';
-import { STATUS_ENABLED, STATUS_REGISTERING } from '../constants';
-
-export const getNameAndDomain = (fullyQualifiedName) => fullyQualifiedName.split('@');
-export const isFullyQualified = (name) => name.indexOf('@') !== -1;
-
-export function isRegisteringOrEnabled() {
- const status = Settings.findOneById('FEDERATION_Status');
- return [STATUS_ENABLED, STATUS_REGISTERING].includes(status && status.value);
-}
-
-export function updateStatus(status) {
- Settings.updateValueById('FEDERATION_Status', status);
-}
-
-export function updateEnabled(enabled) {
- Settings.updateValueById('FEDERATION_Enabled', enabled);
-}
-
-export const checkRoomType = (room) => room.t === 'p' || room.t === 'd';
-export const checkRoomDomainsLength = (domains) => domains.length <= (process.env.FEDERATED_DOMAINS_LENGTH || 10);
-
-export const hasExternalDomain = ({ federation }) => {
- // same test as isFederated(room)
- if (!federation) {
- return false;
- }
-
- return federation.domains
- .some((domain) => domain !== federation.origin);
-};
-
-export const isLocalUser = ({ federation }, localDomain) =>
- !federation || federation.origin === localDomain;
-
-export const getFederatedRoomData = (room) => {
- let hasFederatedUser = false;
-
- let users = null;
- let subscriptions = null;
-
- if (room.t === 'd') {
- // Check if there is a federated user on this room
- hasFederatedUser = room.usernames.some(isFullyQualified);
- } else {
- // Find all subscriptions of this room
- subscriptions = Subscriptions.findByRoomIdWhenUsernameExists(room._id).fetch();
- subscriptions = subscriptions.reduce((acc, s) => {
- acc[s.u._id] = s;
-
- return acc;
- }, {});
-
- // Get all user ids
- const userIds = Object.keys(subscriptions);
-
- // Load all the users
- users = Users.findUsersWithUsernameByIds(userIds).fetch();
-
- // Check if there is a federated user on this room
- hasFederatedUser = users.some((u) => isFullyQualified(u.username));
- }
-
- return {
- hasFederatedUser,
- users,
- subscriptions,
- };
-};
diff --git a/app/federation/server/functions/helpers.ts b/app/federation/server/functions/helpers.ts
new file mode 100644
index 000000000000..e8cb5b3e5170
--- /dev/null
+++ b/app/federation/server/functions/helpers.ts
@@ -0,0 +1,77 @@
+import { IRoom, isDirectMessageRoom } from '../../../../definition/IRoom';
+import { ISubscription } from '../../../../definition/ISubscription';
+import { IRegisterUser, IUser } from '../../../../definition/IUser';
+import { Subscriptions, Users } from '../../../models/server';
+import { Settings } from '../../../models/server/raw';
+import { STATUS_ENABLED, STATUS_REGISTERING } from '../constants';
+
+export const getNameAndDomain = (fullyQualifiedName: string): string [] => fullyQualifiedName.split('@');
+
+export const isFullyQualified = (name: string): boolean => name.indexOf('@') !== -1;
+
+export async function isRegisteringOrEnabled(): Promise {
+ const value = await Settings.getValueById('FEDERATION_Status');
+ return typeof value === 'string' && [STATUS_ENABLED, STATUS_REGISTERING].includes(value);
+}
+
+export async function updateStatus(status: string): Promise {
+ await Settings.updateValueById('FEDERATION_Status', status);
+}
+
+export async function updateEnabled(enabled: boolean): Promise {
+ await Settings.updateValueById('FEDERATION_Enabled', enabled);
+}
+
+export const checkRoomType = (room: IRoom): boolean => room.t === 'p' || room.t === 'd';
+export const checkRoomDomainsLength = (domains: unknown[]): boolean => domains.length <= (process.env.FEDERATED_DOMAINS_LENGTH || 10);
+
+export const hasExternalDomain = ({ federation }: { federation: { origin: string; domains: string[] } }): boolean => {
+ // same test as isFederated(room)
+ if (!federation) {
+ return false;
+ }
+
+ return federation.domains
+ .some((domain) => domain !== federation.origin);
+};
+
+export const isLocalUser = ({ federation }: { federation: { origin: string } }, localDomain: string): boolean =>
+ !federation || federation.origin === localDomain;
+
+export const getFederatedRoomData = (room: IRoom): {
+ hasFederatedUser: boolean;
+ users: IUser[];
+ subscriptions: { [k: string]: ISubscription } | undefined;
+} => {
+ if (isDirectMessageRoom(room)) {
+ // Check if there is a federated user on this room
+
+ return {
+ users: [],
+ hasFederatedUser: room.usernames.some(isFullyQualified),
+ subscriptions: undefined,
+ };
+ }
+
+ // Find all subscriptions of this room
+ const s = Subscriptions.findByRoomIdWhenUsernameExists(room._id).fetch() as ISubscription[];
+ const subscriptions = s.reduce((acc, s) => {
+ acc[s.u._id] = s;
+ return acc;
+ }, {} as { [k: string]: ISubscription });
+
+ // Get all user ids
+ const userIds = Object.keys(subscriptions);
+
+ // Load all the users
+ const users: IRegisterUser[] = Users.findUsersWithUsernameByIds(userIds).fetch();
+
+ // Check if there is a federated user on this room
+ const hasFederatedUser = users.some((u) => isFullyQualified(u.username));
+
+ return {
+ hasFederatedUser,
+ users,
+ subscriptions,
+ };
+};
diff --git a/app/federation/server/handler/index.js b/app/federation/server/handler/index.js
index 7827ddf063a7..46aec0b7dcea 100644
--- a/app/federation/server/handler/index.js
+++ b/app/federation/server/handler/index.js
@@ -5,7 +5,7 @@ import { clientLogger } from '../lib/logger';
import { isFederationEnabled } from '../lib/isFederationEnabled';
import { federationRequestToPeer } from '../lib/http';
-export function federationSearchUsers(query) {
+export async function federationSearchUsers(query) {
if (!isFederationEnabled()) {
throw disabled('client.searchUsers');
}
@@ -16,12 +16,12 @@ export function federationSearchUsers(query) {
const uri = `/api/v1/federation.users.search?${ qs.stringify({ username, domain: peerDomain }) }`;
- const { data: { users } } = federationRequestToPeer('GET', peerDomain, uri);
+ const { data: { users } } = await federationRequestToPeer('GET', peerDomain, uri);
return users;
}
-export function getUserByUsername(query) {
+export async function getUserByUsername(query) {
if (!isFederationEnabled()) {
throw disabled('client.searchUsers');
}
@@ -32,12 +32,12 @@ export function getUserByUsername(query) {
const uri = `/api/v1/federation.users.getByUsername?${ qs.stringify({ username }) }`;
- const { data: { user } } = federationRequestToPeer('GET', peerDomain, uri);
+ const { data: { user } } = await federationRequestToPeer('GET', peerDomain, uri);
return user;
}
-export function requestEventsFromLatest(domain, fromDomain, contextType, contextQuery, latestEventIds) {
+export async function requestEventsFromLatest(domain, fromDomain, contextType, contextQuery, latestEventIds) {
if (!isFederationEnabled()) {
throw disabled('client.requestEventsFromLatest');
}
@@ -46,11 +46,11 @@ export function requestEventsFromLatest(domain, fromDomain, contextType, context
const uri = '/api/v1/federation.events.requestFromLatest';
- federationRequestToPeer('POST', domain, uri, { fromDomain, contextType, contextQuery, latestEventIds });
+ await federationRequestToPeer('POST', domain, uri, { fromDomain, contextType, contextQuery, latestEventIds });
}
-export function dispatchEvents(domains, events) {
+export async function dispatchEvents(domains, events) {
if (!isFederationEnabled()) {
throw disabled('client.dispatchEvents');
}
@@ -61,17 +61,17 @@ export function dispatchEvents(domains, events) {
const uri = '/api/v1/federation.events.dispatch';
- for (const domain of domains) {
- federationRequestToPeer('POST', domain, uri, { events }, { ignoreErrors: true });
+ for await (const domain of domains) {
+ await federationRequestToPeer('POST', domain, uri, { events }, { ignoreErrors: true });
}
}
-export function dispatchEvent(domains, event) {
- dispatchEvents([...new Set(domains)], [event]);
+export async function dispatchEvent(domains, event) {
+ await dispatchEvents([...new Set(domains)], [event]);
}
-export function getUpload(domain, fileId) {
- const { data: { upload, buffer } } = federationRequestToPeer('GET', domain, `/api/v1/federation.uploads?${ qs.stringify({ upload_id: fileId }) }`);
+export async function getUpload(domain, fileId) {
+ const { data: { upload, buffer } } = await federationRequestToPeer('GET', domain, `/api/v1/federation.uploads?${ qs.stringify({ upload_id: fileId }) }`);
return { upload, buffer: Buffer.from(buffer) };
}
diff --git a/app/federation/server/hooks/afterCreateDirectRoom.js b/app/federation/server/hooks/afterCreateDirectRoom.js
index ac05794e1c2e..79e6fc992836 100644
--- a/app/federation/server/hooks/afterCreateDirectRoom.js
+++ b/app/federation/server/hooks/afterCreateDirectRoom.js
@@ -41,7 +41,7 @@ async function afterCreateDirectRoom(room, extras) {
}));
// Dispatch the events
- dispatchEvents(normalizedRoom.federation.domains, [genesisEvent, ...events]);
+ await dispatchEvents(normalizedRoom.federation.domains, [genesisEvent, ...events]);
} catch (err) {
await deleteRoom(room._id);
diff --git a/app/federation/server/hooks/afterCreateRoom.js b/app/federation/server/hooks/afterCreateRoom.js
index 75dfeeac6575..905e108740cf 100644
--- a/app/federation/server/hooks/afterCreateRoom.js
+++ b/app/federation/server/hooks/afterCreateRoom.js
@@ -47,7 +47,7 @@ export async function doAfterCreateRoom(room, users, subscriptions) {
const genesisEvent = await FederationRoomEvents.createGenesisEvent(getFederationDomain(), normalizedRoom);
// Dispatch the events
- dispatchEvents(normalizedRoom.federation.domains, [genesisEvent, ...addUserEvents]);
+ await dispatchEvents(normalizedRoom.federation.domains, [genesisEvent, ...addUserEvents]);
}
async function afterCreateRoom(roomOwner, room) {
diff --git a/app/federation/server/lib/crypt.js b/app/federation/server/lib/crypt.js
index 7a231a13fb91..5a7685a2e9e0 100644
--- a/app/federation/server/lib/crypt.js
+++ b/app/federation/server/lib/crypt.js
@@ -1,19 +1,19 @@
-import { FederationKeys } from '../../../models/server';
+import { FederationKeys } from '../../../models/server/raw';
import { getFederationDomain } from './getFederationDomain';
import { search } from './dns';
import { cryptLogger } from './logger';
-export function decrypt(data, peerKey) {
+export async function decrypt(data, peerKey) {
//
// Decrypt the payload
const payloadBuffer = Buffer.from(data);
// Decrypt with the peer's public key
try {
- data = FederationKeys.loadKey(peerKey, 'public').decryptPublic(payloadBuffer);
+ data = (await FederationKeys.loadKey(peerKey, 'public')).decryptPublic(payloadBuffer);
// Decrypt with the local private key
- data = FederationKeys.getPrivateKey().decrypt(data);
+ data = (await FederationKeys.getPrivateKey()).decrypt(data);
} catch (err) {
cryptLogger.error(err);
@@ -23,7 +23,7 @@ export function decrypt(data, peerKey) {
return JSON.parse(data.toString());
}
-export function decryptIfNeeded(request, bodyParams) {
+export async function decryptIfNeeded(request, bodyParams) {
//
// Look for the domain that sent this event
const remotePeerDomain = request.headers['x-federation-domain'];
@@ -48,17 +48,17 @@ export function decryptIfNeeded(request, bodyParams) {
return decrypt(bodyParams, peerKey);
}
-export function encrypt(data, peerKey) {
+export async function encrypt(data, peerKey) {
if (!data) {
return data;
}
try {
// Encrypt with the peer's public key
- data = FederationKeys.loadKey(peerKey, 'public').encrypt(data);
+ data = (await FederationKeys.loadKey(peerKey, 'public')).encrypt(data);
// Encrypt with the local private key
- return FederationKeys.getPrivateKey().encryptPrivate(data);
+ return (await FederationKeys.getPrivateKey()).encryptPrivate(data);
} catch (err) {
cryptLogger.error(err);
diff --git a/app/federation/server/lib/dns.js b/app/federation/server/lib/dns.js
index 0c4e2f348e1b..0080ddae625b 100644
--- a/app/federation/server/lib/dns.js
+++ b/app/federation/server/lib/dns.js
@@ -17,12 +17,12 @@ const memoizedDnsResolveTXT = mem(dnsResolveTXT, { maxAge: cacheMaxAge });
const hubUrl = process.env.NODE_ENV === 'development' ? 'http://localhost:8080' : 'https://hub.rocket.chat';
-export function registerWithHub(peerDomain, url, publicKey) {
+export async function registerWithHub(peerDomain, url, publicKey) {
const body = { domain: peerDomain, url, public_key: publicKey };
try {
// If there is no DNS entry for that, get from the Hub
- federationRequest('POST', `${ hubUrl }/api/v1/peers`, body);
+ await federationRequest('POST', `${ hubUrl }/api/v1/peers`, body);
return true;
} catch (err) {
@@ -32,12 +32,12 @@ export function registerWithHub(peerDomain, url, publicKey) {
}
}
-export function searchHub(peerDomain) {
+export async function searchHub(peerDomain) {
try {
dnsLogger.debug(`searchHub: peerDomain=${ peerDomain }`);
// If there is no DNS entry for that, get from the Hub
- const { data: { peer } } = federationRequest('GET', `${ hubUrl }/api/v1/peers?search=${ peerDomain }`);
+ const { data: { peer } } = await federationRequest('GET', `${ hubUrl }/api/v1/peers?search=${ peerDomain }`);
if (!peer) {
dnsLogger.debug(`searchHub: could not find peerDomain=${ peerDomain }`);
diff --git a/app/federation/server/lib/http.js b/app/federation/server/lib/http.js
index 542a2d32ef9e..e18d09b8e86d 100644
--- a/app/federation/server/lib/http.js
+++ b/app/federation/server/lib/http.js
@@ -6,14 +6,14 @@ import { getFederationDomain } from './getFederationDomain';
import { search } from './dns';
import { encrypt } from './crypt';
-export function federationRequest(method, url, body, headers, peerKey = null) {
+export async function federationRequest(method, url, body, headers, peerKey = null) {
let data = null;
if ((method === 'POST' || method === 'PUT') && body) {
data = EJSON.toJSONValue(body);
if (peerKey) {
- data = encrypt(data, peerKey);
+ data = await encrypt(data, peerKey);
}
}
@@ -22,7 +22,7 @@ export function federationRequest(method, url, body, headers, peerKey = null) {
return MeteorHTTP.call(method, url, { data, timeout: 2000, headers: { ...headers, 'x-federation-domain': getFederationDomain() } });
}
-export function federationRequestToPeer(method, peerDomain, uri, body, options = {}) {
+export async function federationRequestToPeer(method, peerDomain, uri, body, options = {}) {
const ignoreErrors = peerDomain === getFederationDomain() ? false : options.ignoreErrors;
const { url: baseUrl, publicKey } = search(peerDomain);
@@ -39,7 +39,7 @@ export function federationRequestToPeer(method, peerDomain, uri, body, options =
try {
httpLogger.debug({ msg: 'federationRequestToPeer', url: `${ baseUrl }${ uri }` });
- result = federationRequest(method, `${ baseUrl }${ uri }`, body, options.headers || {}, peerKey);
+ result = await federationRequest(method, `${ baseUrl }${ uri }`, body, options.headers || {}, peerKey);
} catch (err) {
httpLogger.error({ msg: `${ ignoreErrors ? '[IGNORED] ' : '' }Error`, err });
diff --git a/app/federation/server/startup/generateKeys.js b/app/federation/server/startup/generateKeys.js
index 012cdd0b48f4..32eaacc30418 100644
--- a/app/federation/server/startup/generateKeys.js
+++ b/app/federation/server/startup/generateKeys.js
@@ -1,6 +1,8 @@
-import { FederationKeys } from '../../../models/server';
+import { FederationKeys } from '../../../models/server/raw';
// Create key pair if needed
-if (!FederationKeys.getPublicKey()) {
- FederationKeys.generateKeys();
-}
+(async () => {
+ if (!await FederationKeys.getPublicKey()) {
+ await FederationKeys.generateKeys();
+ }
+})();
diff --git a/app/federation/server/startup/settings.ts b/app/federation/server/startup/settings.ts
index cfa7fda19e6a..36ade9e70eeb 100644
--- a/app/federation/server/startup/settings.ts
+++ b/app/federation/server/startup/settings.ts
@@ -7,11 +7,11 @@ import { getFederationDiscoveryMethod } from '../lib/getFederationDiscoveryMetho
import { registerWithHub } from '../lib/dns';
import { enableCallbacks, disableCallbacks } from '../lib/callbacks';
import { setupLogger } from '../lib/logger';
-import { FederationKeys } from '../../../models/server';
+import { FederationKeys } from '../../../models/server/raw';
import { STATUS_ENABLED, STATUS_REGISTERING, STATUS_ERROR_REGISTERING, STATUS_DISABLED } from '../constants';
-Meteor.startup(function() {
- const federationPublicKey = FederationKeys.getPublicKeyString();
+Meteor.startup(async function() {
+ const federationPublicKey = await FederationKeys.getPublicKeyString();
settingsRegistry.addGroup('Federation', function() {
this.add('FEDERATION_Enabled', false, {
@@ -36,7 +36,7 @@ Meteor.startup(function() {
// disableReset: true,
});
- this.add('FEDERATION_Public_Key', federationPublicKey, {
+ this.add('FEDERATION_Public_Key', federationPublicKey || '', {
readonly: true,
type: 'string',
multiline: true,
@@ -65,26 +65,26 @@ Meteor.startup(function() {
});
});
-const updateSettings = function(): void {
+const updateSettings = async function(): Promise {
// Get the key pair
- if (getFederationDiscoveryMethod() === 'hub' && !isRegisteringOrEnabled()) {
+ if (getFederationDiscoveryMethod() === 'hub' && !Promise.await(isRegisteringOrEnabled())) {
// Register with hub
try {
- updateStatus(STATUS_REGISTERING);
+ await updateStatus(STATUS_REGISTERING);
- registerWithHub(getFederationDomain(), settings.get('Site_Url'), FederationKeys.getPublicKeyString());
+ await registerWithHub(getFederationDomain(), settings.get('Site_Url'), await FederationKeys.getPublicKeyString());
- updateStatus(STATUS_ENABLED);
+ await updateStatus(STATUS_ENABLED);
} catch (err) {
// Disable federation
- updateEnabled(false);
+ await updateEnabled(false);
- updateStatus(STATUS_ERROR_REGISTERING);
+ await updateStatus(STATUS_ERROR_REGISTERING);
}
- } else {
- updateStatus(STATUS_ENABLED);
+ return;
}
+ await updateStatus(STATUS_ENABLED);
};
// Add settings listeners
@@ -92,11 +92,11 @@ settings.watch('FEDERATION_Enabled', function enableOrDisable(value) {
setupLogger.info(`Federation is ${ value ? 'enabled' : 'disabled' }`);
if (value) {
- updateSettings();
+ Promise.await(updateSettings());
enableCallbacks();
} else {
- updateStatus(STATUS_DISABLED);
+ Promise.await(updateStatus(STATUS_DISABLED));
disableCallbacks();
}
diff --git a/app/file-upload/server/lib/FileUpload.js b/app/file-upload/server/lib/FileUpload.js
index aa8d49ab408e..70ecac27f203 100644
--- a/app/file-upload/server/lib/FileUpload.js
+++ b/app/file-upload/server/lib/FileUpload.js
@@ -2,6 +2,7 @@ import fs from 'fs';
import stream from 'stream';
import { Meteor } from 'meteor/meteor';
+import { Mongo } from 'meteor/mongo';
import streamBuffers from 'stream-buffers';
import Future from 'fibers/future';
import sharp from 'sharp';
@@ -13,9 +14,7 @@ import filesize from 'filesize';
import { AppsEngineException } from '@rocket.chat/apps-engine/definition/exceptions';
import { settings } from '../../../settings/server';
-import Uploads from '../../../models/server/models/Uploads';
-import UserDataFiles from '../../../models/server/models/UserDataFiles';
-import Avatars from '../../../models/server/models/Avatars';
+import { Avatars, UserDataFiles, Uploads } from '../../../models/server/raw';
import Users from '../../../models/server/models/Users';
import Rooms from '../../../models/server/models/Rooms';
import Settings from '../../../models/server/models/Settings';
@@ -41,6 +40,9 @@ settings.watch('FileUpload_MaxFileSize', function(value) {
}
});
+const AvatarModel = new Mongo.Collection(Avatars.col.collectionName);
+const UserDataFilesModel = new Mongo.Collection(UserDataFiles.col.collectionName);
+const UploadsModel = new Mongo.Collection(Uploads.col.collectionName);
export const FileUpload = {
handlers: {},
@@ -139,7 +141,7 @@ export const FileUpload = {
defaultUploads() {
return {
- collection: Uploads.model,
+ collection: UploadsModel,
filter: new UploadFS.Filter({
onCheck: FileUpload.validateFileUpload,
}),
@@ -161,7 +163,7 @@ export const FileUpload = {
defaultAvatars() {
return {
- collection: Avatars.model,
+ collection: AvatarModel,
filter: new UploadFS.Filter({
onCheck: FileUpload.validateAvatarUpload,
}),
@@ -176,7 +178,7 @@ export const FileUpload = {
defaultUserDataFiles() {
return {
- collection: UserDataFiles.model,
+ collection: UserDataFilesModel,
getPath(file) {
return `${ settings.get('uniqueID') }/uploads/userData/${ file.userId }`;
},
@@ -254,7 +256,7 @@ export const FileUpload = {
},
resizeImagePreview(file) {
- file = Uploads.findOneById(file._id);
+ file = Promise.await(Uploads.findOneById(file._id));
file = FileUpload.addExtensionTo(file);
const image = FileUpload.getStore('Uploads')._store.getReadStream(file._id, file);
@@ -279,7 +281,7 @@ export const FileUpload = {
return;
}
- file = Uploads.findOneById(file._id);
+ file = Promise.await(Uploads.findOneById(file._id));
file = FileUpload.addExtensionTo(file);
const store = FileUpload.getStore('Uploads');
const image = store._store.getReadStream(file._id, file);
@@ -378,11 +380,11 @@ export const FileUpload = {
}
// update file record to match user's username
const user = Users.findOneById(file.userId);
- const oldAvatar = Avatars.findOneByName(user.username);
+ const oldAvatar = Promise.await(Avatars.findOneByName(user.username));
if (oldAvatar) {
- Avatars.deleteFile(oldAvatar._id);
+ Promise.await(Avatars.deleteFile(oldAvatar._id));
}
- Avatars.updateFileNameById(file._id, user.username);
+ Promise.await(Avatars.updateFileNameById(file._id, user.username));
// console.log('upload finished ->', file);
},
@@ -567,15 +569,16 @@ export class FileUploadClass {
}
delete(fileId) {
+ // TODO: Remove this method
if (this.store && this.store.delete) {
this.store.delete(fileId);
}
- return this.model.deleteFile(fileId);
+ return Promise.await(this.model.deleteFile(fileId));
}
deleteById(fileId) {
- const file = this.model.findOneById(fileId);
+ const file = Promise.await(this.model.findOneById(fileId));
if (!file) {
return;
@@ -587,7 +590,7 @@ export class FileUploadClass {
}
deleteByName(fileName) {
- const file = this.model.findOneByName(fileName);
+ const file = Promise.await(this.model.findOneByName(fileName));
if (!file) {
return;
@@ -600,7 +603,7 @@ export class FileUploadClass {
deleteByRoomId(rid) {
- const file = this.model.findOneByRoomId(rid);
+ const file = Promise.await(this.model.findOneByRoomId(rid));
if (!file) {
return;
diff --git a/app/file-upload/server/lib/requests.js b/app/file-upload/server/lib/requests.js
index 80a3b4213b38..3b2e8dad19d7 100644
--- a/app/file-upload/server/lib/requests.js
+++ b/app/file-upload/server/lib/requests.js
@@ -1,13 +1,13 @@
import { WebApp } from 'meteor/webapp';
import { FileUpload } from './FileUpload';
-import { Uploads } from '../../../models';
+import { Uploads } from '../../../models/server/raw';
-WebApp.connectHandlers.use(FileUpload.getPath(), function(req, res, next) {
+WebApp.connectHandlers.use(FileUpload.getPath(), async function(req, res, next) {
const match = /^\/([^\/]+)\/(.*)/.exec(req.url);
if (match && match[1]) {
- const file = Uploads.findOneById(match[1]);
+ const file = await Uploads.findOneById(match[1]);
if (file) {
if (!FileUpload.requestCanAccessFiles(req)) {
diff --git a/app/file-upload/server/methods/getS3FileUrl.js b/app/file-upload/server/methods/getS3FileUrl.js
index f68f720d171b..cfffdfcc032a 100644
--- a/app/file-upload/server/methods/getS3FileUrl.js
+++ b/app/file-upload/server/methods/getS3FileUrl.js
@@ -2,7 +2,7 @@ import { Meteor } from 'meteor/meteor';
import { UploadFS } from 'meteor/jalik:ufs';
import { settings } from '../../../settings/server';
-import { Uploads } from '../../../models';
+import { Uploads } from '../../../models/server/raw';
let protectedFiles;
@@ -11,11 +11,11 @@ settings.watch('FileUpload_ProtectFiles', function(value) {
});
Meteor.methods({
- getS3FileUrl(fileId) {
+ async getS3FileUrl(fileId) {
if (protectedFiles && !Meteor.userId()) {
throw new Meteor.Error('error-invalid-user', 'Invalid user', { method: 'sendFileMessage' });
}
- const file = Uploads.findOneById(fileId);
+ const file = await Uploads.findOneById(fileId);
return UploadFS.getStore('AmazonS3:Uploads').getRedirectURL(file);
},
diff --git a/app/file-upload/server/methods/sendFileMessage.ts b/app/file-upload/server/methods/sendFileMessage.ts
index 80dec7bca683..886f9167e07e 100644
--- a/app/file-upload/server/methods/sendFileMessage.ts
+++ b/app/file-upload/server/methods/sendFileMessage.ts
@@ -3,8 +3,7 @@ import { Meteor } from 'meteor/meteor';
import { Match, check } from 'meteor/check';
import _ from 'underscore';
-import { Uploads } from '../../../models/server';
-import { Rooms } from '../../../models/server/raw';
+import { Rooms, Uploads } from '../../../models/server/raw';
import { callbacks } from '../../../callbacks/server';
import { FileUpload } from '../lib/FileUpload';
import { canAccessRoom } from '../../../authorization/server/functions/canAccessRoom';
@@ -35,7 +34,7 @@ Meteor.methods({
tmid: Match.Optional(String),
});
- Uploads.updateFileComplete(file._id, user._id, _.omit(file, '_id'));
+ await Uploads.updateFileComplete(file._id, user._id, _.omit(file, '_id'));
const fileUrl = FileUpload.getPath(`${ file._id }/${ encodeURI(file.name) }`);
diff --git a/app/highlight-words/tests/helper.tests.js b/app/highlight-words/tests/helper.tests.js
index 28c5fd075164..2b4e895d0e65 100644
--- a/app/highlight-words/tests/helper.tests.js
+++ b/app/highlight-words/tests/helper.tests.js
@@ -1,7 +1,4 @@
-/* eslint-env mocha */
-
-import 'babel-polyfill';
-import assert from 'assert';
+import { expect } from 'chai';
import { highlightWords, getRegexHighlight, getRegexHighlightUrl } from '../client/helper';
@@ -14,7 +11,7 @@ describe('helper', () => {
urlRegex: getRegexHighlightUrl(highlight),
})));
- assert.equal(res, 'here is some word');
+ expect(res).to.be.equal('here is some word');
});
describe('handles links', () => {
@@ -25,7 +22,7 @@ describe('helper', () => {
urlRegex: getRegexHighlightUrl(highlight),
})));
- assert.equal(res, 'here we go https://somedomain.com/here-some.word/pulls more words after');
+ expect(res).to.be.equal('here we go https://somedomain.com/here-some.word/pulls more words after');
});
it('not highlighting two links', () => {
@@ -36,7 +33,7 @@ describe('helper', () => {
urlRegex: getRegexHighlightUrl(highlight),
})));
- assert.equal(res, msg);
+ expect(res).to.be.equal(msg);
});
it('not highlighting link but keep words on message highlighted', () => {
@@ -46,7 +43,7 @@ describe('helper', () => {
urlRegex: getRegexHighlightUrl(highlight),
})));
- assert.equal(res, 'here we go https://somedomain.com/here-some.foo/pulls more foo after');
+ expect(res).to.be.equal('here we go https://somedomain.com/here-some.foo/pulls more foo after');
});
});
});
diff --git a/app/integrations/server/api/api.js b/app/integrations/server/api/api.js
index 17a04b6c3582..eb223c67c9ad 100644
--- a/app/integrations/server/api/api.js
+++ b/app/integrations/server/api/api.js
@@ -14,6 +14,7 @@ import { incomingLogger } from '../logger';
import { processWebhookMessage } from '../../../lib/server';
import { API, APIClass, defaultRateLimiterOptions } from '../../../api/server';
import * as Models from '../../../models/server';
+import { Integrations } from '../../../models/server/raw';
import { settings } from '../../../settings/server';
const compiledScripts = {};
@@ -129,9 +130,10 @@ function removeIntegration(options, user) {
incomingLogger.info('Remove integration');
incomingLogger.debug(options);
- const integrationToRemove = Models.Integrations.findOne({
- urls: options.target_url,
- });
+ const integrationToRemove = Promise.await(Integrations.findOneByUrl(options.target_url));
+ if (!integrationToRemove) {
+ return API.v1.failure('integration-not-found');
+ }
Meteor.runAsUser(user._id, () => Meteor.call('deleteOutgoingIntegration', integrationToRemove._id));
@@ -373,10 +375,10 @@ const Api = new WebHookAPI({
}
}
- this.integration = Models.Integrations.findOne({
+ this.integration = Promise.await(Integrations.findOne({
_id: this.request.params.integrationId,
token: decodeURIComponent(this.request.params.token),
- });
+ }));
if (!this.integration) {
incomingLogger.info(`Invalid integration id ${ this.request.params.integrationId } or token ${ this.request.params.token }`);
diff --git a/app/integrations/server/lib/triggerHandler.js b/app/integrations/server/lib/triggerHandler.js
index 33cc33ddb24d..27f71a4e5729 100644
--- a/app/integrations/server/lib/triggerHandler.js
+++ b/app/integrations/server/lib/triggerHandler.js
@@ -10,6 +10,7 @@ import Fiber from 'fibers';
import Future from 'fibers/future';
import * as Models from '../../../models/server';
+import { Integrations, IntegrationHistory } from '../../../models/server/raw';
import { settings } from '../../../settings/server';
import { getRoomByNameOrIdWithOptionToJoin, processWebhookMessage } from '../../../lib/server';
import { outgoingLogger } from '../logger';
@@ -22,7 +23,7 @@ export class RocketChatIntegrationHandler {
this.compiledScripts = {};
this.triggers = {};
- Models.Integrations.find({ type: 'webhook-outgoing' }).fetch().forEach((data) => this.addIntegration(data));
+ Promise.await(Integrations.find({ type: 'webhook-outgoing' }).forEach((data) => this.addIntegration(data)));
}
addIntegration(record) {
@@ -142,11 +143,11 @@ export class RocketChatIntegrationHandler {
}
if (historyId) {
- Models.IntegrationHistory.update({ _id: historyId }, { $set: history });
+ Promise.await(IntegrationHistory.updateOne({ _id: historyId }, { $set: history }));
return historyId;
}
history._createdAt = new Date();
- return Models.IntegrationHistory.insert(Object.assign({ _id: Random.id() }, history));
+ return Promise.await(IntegrationHistory.insertOne({ _id: Random.id(), ...history }));
}
// Trigger is the trigger, nameOrId is a string which is used to try and find a room, room is a room, message is a message, and data contains "user_name" if trigger.impersonateUser is truthful.
@@ -715,7 +716,7 @@ export class RocketChatIntegrationHandler {
if (result.statusCode === 410) {
this.updateHistory({ historyId, step: 'after-process-http-status-410', error: true });
outgoingLogger.error(`Disabling the Integration "${ trigger.name }" because the status code was 401 (Gone).`);
- Models.Integrations.update({ _id: trigger._id }, { $set: { enabled: false } });
+ Promise.await(Integrations.updateOne({ _id: trigger._id }, { $set: { enabled: false } }));
return;
}
diff --git a/app/integrations/server/methods/clearIntegrationHistory.js b/app/integrations/server/methods/clearIntegrationHistory.ts
similarity index 70%
rename from app/integrations/server/methods/clearIntegrationHistory.js
rename to app/integrations/server/methods/clearIntegrationHistory.ts
index 87eec581e37a..f4ef3e974b96 100644
--- a/app/integrations/server/methods/clearIntegrationHistory.js
+++ b/app/integrations/server/methods/clearIntegrationHistory.ts
@@ -1,17 +1,20 @@
import { Meteor } from 'meteor/meteor';
-import { hasPermission } from '../../../authorization';
-import { IntegrationHistory, Integrations } from '../../../models';
+import { hasPermission } from '../../../authorization/server';
+import { IntegrationHistory, Integrations } from '../../../models/server/raw';
import notifications from '../../../notifications/server/lib/Notifications';
Meteor.methods({
- clearIntegrationHistory(integrationId) {
+ async clearIntegrationHistory(integrationId) {
let integration;
if (hasPermission(this.userId, 'manage-outgoing-integrations') || hasPermission(this.userId, 'manage-outgoing-integrations', 'bot')) {
- integration = Integrations.findOne(integrationId);
+ integration = await Integrations.findOneById(integrationId);
} else if (hasPermission(this.userId, 'manage-own-outgoing-integrations') || hasPermission(this.userId, 'manage-own-outgoing-integrations', 'bot')) {
- integration = Integrations.findOne(integrationId, { fields: { '_createdBy._id': this.userId } });
+ integration = await Integrations.findOne({
+ _id: integrationId,
+ '_createdBy._id': this.userId,
+ });
} else {
throw new Meteor.Error('not_authorized', 'Unauthorized', { method: 'clearIntegrationHistory' });
}
@@ -20,7 +23,7 @@ Meteor.methods({
throw new Meteor.Error('error-invalid-integration', 'Invalid integration', { method: 'clearIntegrationHistory' });
}
- IntegrationHistory.removeByIntegrationId(integrationId);
+ await IntegrationHistory.removeByIntegrationId(integrationId);
notifications.streamIntegrationHistory.emit(integrationId, { type: 'removed' });
diff --git a/app/integrations/server/methods/incoming/addIncomingIntegration.js b/app/integrations/server/methods/incoming/addIncomingIntegration.js
index 6e86dacd5700..23b339ed48fb 100644
--- a/app/integrations/server/methods/incoming/addIncomingIntegration.js
+++ b/app/integrations/server/methods/incoming/addIncomingIntegration.js
@@ -4,13 +4,14 @@ import { Babel } from 'meteor/babel-compiler';
import _ from 'underscore';
import s from 'underscore.string';
-import { hasPermission, hasAllPermission } from '../../../../authorization';
-import { Users, Rooms, Integrations, Roles, Subscriptions } from '../../../../models';
+import { hasPermission, hasAllPermission } from '../../../../authorization/server';
+import { Users, Rooms, Subscriptions } from '../../../../models/server';
+import { Integrations, Roles } from '../../../../models/server/raw';
const validChannelChars = ['@', '#'];
Meteor.methods({
- addIncomingIntegration(integration) {
+ async addIncomingIntegration(integration) {
if (!hasPermission(this.userId, 'manage-incoming-integrations') && !hasPermission(this.userId, 'manage-own-incoming-integrations')) {
throw new Meteor.Error('not_authorized', 'Unauthorized', { method: 'addIncomingIntegration' });
}
@@ -95,9 +96,11 @@ Meteor.methods({
integration._createdAt = new Date();
integration._createdBy = Users.findOne(this.userId, { fields: { username: 1 } });
- Roles.addUserRoles(user._id, 'bot');
+ await Roles.addUserRoles(user._id, 'bot');
- integration._id = Integrations.insert(integration);
+ const result = await Integrations.insertOne(integration);
+
+ integration._id = result.insertedId;
return integration;
},
diff --git a/app/integrations/server/methods/incoming/deleteIncomingIntegration.js b/app/integrations/server/methods/incoming/deleteIncomingIntegration.ts
similarity index 56%
rename from app/integrations/server/methods/incoming/deleteIncomingIntegration.js
rename to app/integrations/server/methods/incoming/deleteIncomingIntegration.ts
index 96c25116a10d..bbd158f20ae0 100644
--- a/app/integrations/server/methods/incoming/deleteIncomingIntegration.js
+++ b/app/integrations/server/methods/incoming/deleteIncomingIntegration.ts
@@ -1,16 +1,19 @@
import { Meteor } from 'meteor/meteor';
-import { hasPermission } from '../../../../authorization';
-import { Integrations } from '../../../../models';
+import { hasPermission } from '../../../../authorization/server';
+import { Integrations } from '../../../../models/server/raw';
Meteor.methods({
- deleteIncomingIntegration(integrationId) {
+ async deleteIncomingIntegration(integrationId) {
let integration;
if (hasPermission(this.userId, 'manage-incoming-integrations')) {
- integration = Integrations.findOne(integrationId);
+ integration = Integrations.findOneById(integrationId);
} else if (hasPermission(this.userId, 'manage-own-incoming-integrations')) {
- integration = Integrations.findOne(integrationId, { fields: { '_createdBy._id': this.userId } });
+ integration = Integrations.findOne({
+ _id: integrationId,
+ '_createdBy._id': this.userId,
+ });
} else {
throw new Meteor.Error('not_authorized', 'Unauthorized', { method: 'deleteIncomingIntegration' });
}
@@ -19,7 +22,7 @@ Meteor.methods({
throw new Meteor.Error('error-invalid-integration', 'Invalid integration', { method: 'deleteIncomingIntegration' });
}
- Integrations.remove({ _id: integrationId });
+ await Integrations.removeById(integrationId);
return true;
},
diff --git a/app/integrations/server/methods/incoming/updateIncomingIntegration.js b/app/integrations/server/methods/incoming/updateIncomingIntegration.js
index 5e7b3517ba0e..fc5a6d384b95 100644
--- a/app/integrations/server/methods/incoming/updateIncomingIntegration.js
+++ b/app/integrations/server/methods/incoming/updateIncomingIntegration.js
@@ -3,13 +3,14 @@ import { Babel } from 'meteor/babel-compiler';
import _ from 'underscore';
import s from 'underscore.string';
-import { Integrations, Rooms, Users, Roles, Subscriptions } from '../../../../models';
-import { hasAllPermission, hasPermission } from '../../../../authorization';
+import { Rooms, Users, Subscriptions } from '../../../../models/server';
+import { Integrations, Roles } from '../../../../models/server/raw';
+import { hasAllPermission, hasPermission } from '../../../../authorization/server';
const validChannelChars = ['@', '#'];
Meteor.methods({
- updateIncomingIntegration(integrationId, integration) {
+ async updateIncomingIntegration(integrationId, integration) {
if (!_.isString(integration.channel) || integration.channel.trim() === '') {
throw new Meteor.Error('error-invalid-channel', 'Invalid channel', { method: 'updateIncomingIntegration' });
}
@@ -25,9 +26,9 @@ Meteor.methods({
let currentIntegration;
if (hasPermission(this.userId, 'manage-incoming-integrations')) {
- currentIntegration = Integrations.findOne(integrationId);
+ currentIntegration = await Integrations.findOneById(integrationId);
} else if (hasPermission(this.userId, 'manage-own-incoming-integrations')) {
- currentIntegration = Integrations.findOne({ _id: integrationId, '_createdBy._id': this.userId });
+ currentIntegration = await Integrations.findOne({ _id: integrationId, '_createdBy._id': this.userId });
} else {
throw new Meteor.Error('not_authorized', 'Unauthorized', { method: 'updateIncomingIntegration' });
}
@@ -43,14 +44,14 @@ Meteor.methods({
integration.scriptCompiled = Babel.compile(integration.script, babelOptions).code;
integration.scriptError = undefined;
- Integrations.update(integrationId, {
+ await Integrations.updateOne({ _id: integrationId }, {
$set: { scriptCompiled: integration.scriptCompiled },
$unset: { scriptError: 1 },
});
} catch (e) {
integration.scriptCompiled = undefined;
integration.scriptError = _.pick(e, 'name', 'message', 'stack');
- Integrations.update(integrationId, {
+ await Integrations.updateOne({ _id: integrationId }, {
$set: {
scriptError: integration.scriptError,
},
@@ -100,9 +101,9 @@ Meteor.methods({
throw new Meteor.Error('error-invalid-post-as-user', 'Invalid Post As User', { method: 'updateIncomingIntegration' });
}
- Roles.addUserRoles(user._id, 'bot');
+ await Roles.addUserRoles(user._id, 'bot');
- Integrations.update(integrationId, {
+ await Integrations.updateOne({ _id: integrationId }, {
$set: {
enabled: integration.enabled,
name: integration.name,
@@ -117,6 +118,6 @@ Meteor.methods({
},
});
- return Integrations.findOne(integrationId);
+ return Integrations.findOneById(integrationId);
},
});
diff --git a/app/integrations/server/methods/outgoing/addOutgoingIntegration.js b/app/integrations/server/methods/outgoing/addOutgoingIntegration.js
index 5baf6e88cda4..ae6f1aa6933d 100644
--- a/app/integrations/server/methods/outgoing/addOutgoingIntegration.js
+++ b/app/integrations/server/methods/outgoing/addOutgoingIntegration.js
@@ -1,11 +1,12 @@
import { Meteor } from 'meteor/meteor';
-import { hasPermission } from '../../../../authorization';
-import { Users, Integrations } from '../../../../models';
+import { hasPermission } from '../../../../authorization/server';
+import { Users } from '../../../../models/server';
+import { Integrations } from '../../../../models/server/raw';
import { integrations } from '../../../lib/rocketchat';
Meteor.methods({
- addOutgoingIntegration(integration) {
+ async addOutgoingIntegration(integration) {
if (!hasPermission(this.userId, 'manage-outgoing-integrations')
&& !hasPermission(this.userId, 'manage-own-outgoing-integrations')
&& !hasPermission(this.userId, 'manage-outgoing-integrations', 'bot')
@@ -17,7 +18,9 @@ Meteor.methods({
integration._createdAt = new Date();
integration._createdBy = Users.findOne(this.userId, { fields: { username: 1 } });
- integration._id = Integrations.insert(integration);
+
+ const result = await Integrations.insertOne(integration);
+ integration._id = result.insertedId;
return integration;
},
diff --git a/app/integrations/server/methods/outgoing/deleteOutgoingIntegration.js b/app/integrations/server/methods/outgoing/deleteOutgoingIntegration.ts
similarity index 63%
rename from app/integrations/server/methods/outgoing/deleteOutgoingIntegration.js
rename to app/integrations/server/methods/outgoing/deleteOutgoingIntegration.ts
index 07823b22bb2c..a63e845eaa77 100644
--- a/app/integrations/server/methods/outgoing/deleteOutgoingIntegration.js
+++ b/app/integrations/server/methods/outgoing/deleteOutgoingIntegration.ts
@@ -1,16 +1,19 @@
import { Meteor } from 'meteor/meteor';
-import { hasPermission } from '../../../../authorization';
-import { IntegrationHistory, Integrations } from '../../../../models';
+import { hasPermission } from '../../../../authorization/server';
+import { IntegrationHistory, Integrations } from '../../../../models/server/raw';
Meteor.methods({
- deleteOutgoingIntegration(integrationId) {
+ async deleteOutgoingIntegration(integrationId) {
let integration;
if (hasPermission(this.userId, 'manage-outgoing-integrations') || hasPermission(this.userId, 'manage-outgoing-integrations', 'bot')) {
- integration = Integrations.findOne(integrationId);
+ integration = Integrations.findOneById(integrationId);
} else if (hasPermission(this.userId, 'manage-own-outgoing-integrations') || hasPermission(this.userId, 'manage-own-outgoing-integrations', 'bot')) {
- integration = Integrations.findOne(integrationId, { fields: { '_createdBy._id': this.userId } });
+ integration = Integrations.findOne({
+ _id: integrationId,
+ '_createdBy._id': this.userId,
+ });
} else {
throw new Meteor.Error('not_authorized', 'Unauthorized', { method: 'deleteOutgoingIntegration' });
}
@@ -19,8 +22,8 @@ Meteor.methods({
throw new Meteor.Error('error-invalid-integration', 'Invalid integration', { method: 'deleteOutgoingIntegration' });
}
- Integrations.remove({ _id: integrationId });
- IntegrationHistory.removeByIntegrationId(integrationId);
+ await Integrations.removeById(integrationId);
+ await IntegrationHistory.removeByIntegrationId(integrationId);
return true;
},
diff --git a/app/integrations/server/methods/outgoing/replayOutgoingIntegration.js b/app/integrations/server/methods/outgoing/replayOutgoingIntegration.ts
similarity index 69%
rename from app/integrations/server/methods/outgoing/replayOutgoingIntegration.js
rename to app/integrations/server/methods/outgoing/replayOutgoingIntegration.ts
index 8d88cde3ea28..bf3136525bc9 100644
--- a/app/integrations/server/methods/outgoing/replayOutgoingIntegration.js
+++ b/app/integrations/server/methods/outgoing/replayOutgoingIntegration.ts
@@ -1,17 +1,20 @@
import { Meteor } from 'meteor/meteor';
-import { hasPermission } from '../../../../authorization';
-import { Integrations, IntegrationHistory } from '../../../../models';
+import { hasPermission } from '../../../../authorization/server';
+import { Integrations, IntegrationHistory } from '../../../../models/server/raw';
import { triggerHandler } from '../../lib/triggerHandler';
Meteor.methods({
- replayOutgoingIntegration({ integrationId, historyId }) {
+ async replayOutgoingIntegration({ integrationId, historyId }) {
let integration;
if (hasPermission(this.userId, 'manage-outgoing-integrations') || hasPermission(this.userId, 'manage-outgoing-integrations', 'bot')) {
- integration = Integrations.findOne(integrationId);
+ integration = await Integrations.findOneById(integrationId);
} else if (hasPermission(this.userId, 'manage-own-outgoing-integrations') || hasPermission(this.userId, 'manage-own-outgoing-integrations', 'bot')) {
- integration = Integrations.findOne(integrationId, { fields: { '_createdBy._id': this.userId } });
+ integration = await Integrations.findOne({
+ _id: integrationId,
+ '_createdBy._id': this.userId,
+ });
} else {
throw new Meteor.Error('not_authorized', 'Unauthorized', { method: 'replayOutgoingIntegration' });
}
@@ -20,7 +23,7 @@ Meteor.methods({
throw new Meteor.Error('error-invalid-integration', 'Invalid integration', { method: 'replayOutgoingIntegration' });
}
- const history = IntegrationHistory.findOneByIntegrationIdAndHistoryId(integration._id, historyId);
+ const history = await IntegrationHistory.findOneByIntegrationIdAndHistoryId(integration._id, historyId);
if (!history) {
throw new Meteor.Error('error-invalid-integration-history', 'Invalid Integration History', { method: 'replayOutgoingIntegration' });
diff --git a/app/integrations/server/methods/outgoing/updateOutgoingIntegration.js b/app/integrations/server/methods/outgoing/updateOutgoingIntegration.js
index 981a7890bc29..e9e4bc1ba968 100644
--- a/app/integrations/server/methods/outgoing/updateOutgoingIntegration.js
+++ b/app/integrations/server/methods/outgoing/updateOutgoingIntegration.js
@@ -1,11 +1,12 @@
import { Meteor } from 'meteor/meteor';
-import { hasPermission } from '../../../../authorization';
-import { Integrations, Users } from '../../../../models';
+import { hasPermission } from '../../../../authorization/server';
+import { Users } from '../../../../models/server';
+import { Integrations } from '../../../../models/server/raw';
import { integrations } from '../../../lib/rocketchat';
Meteor.methods({
- updateOutgoingIntegration(integrationId, integration) {
+ async updateOutgoingIntegration(integrationId, integration) {
integration = integrations.validateOutgoing(integration, this.userId);
if (!integration.token || integration.token.trim() === '') {
@@ -15,9 +16,9 @@ Meteor.methods({
let currentIntegration;
if (hasPermission(this.userId, 'manage-outgoing-integrations')) {
- currentIntegration = Integrations.findOne(integrationId);
+ currentIntegration = await Integrations.findOneById(integrationId);
} else if (hasPermission(this.userId, 'manage-own-outgoing-integrations')) {
- currentIntegration = Integrations.findOne({ _id: integrationId, '_createdBy._id': this.userId });
+ currentIntegration = await Integrations.findOne({ _id: integrationId, '_createdBy._id': this.userId });
} else {
throw new Meteor.Error('not_authorized', 'Unauthorized', { method: 'updateOutgoingIntegration' });
}
@@ -26,18 +27,18 @@ Meteor.methods({
throw new Meteor.Error('invalid_integration', '[methods] updateOutgoingIntegration -> integration not found');
}
if (integration.scriptCompiled) {
- Integrations.update(integrationId, {
+ await Integrations.updateOne({ _id: integrationId }, {
$set: { scriptCompiled: integration.scriptCompiled },
$unset: { scriptError: 1 },
});
} else {
- Integrations.update(integrationId, {
+ await Integrations.updateOne({ _id: integrationId }, {
$set: { scriptError: integration.scriptError },
$unset: { scriptCompiled: 1 },
});
}
- Integrations.update(integrationId, {
+ await Integrations.updateOne({ _id: integrationId }, {
$set: {
event: integration.event,
enabled: integration.enabled,
@@ -65,6 +66,6 @@ Meteor.methods({
},
});
- return Integrations.findOne(integrationId);
+ return Integrations.findOneById(integrationId);
},
});
diff --git a/app/invites/server/functions/findOrCreateInvite.js b/app/invites/server/functions/findOrCreateInvite.js
index c875a53906d0..3b608e7121e0 100644
--- a/app/invites/server/functions/findOrCreateInvite.js
+++ b/app/invites/server/functions/findOrCreateInvite.js
@@ -3,7 +3,8 @@ import { Random } from 'meteor/random';
import { hasPermission } from '../../../authorization';
import { Notifications } from '../../../notifications';
-import { Invites, Subscriptions, Rooms } from '../../../models/server';
+import { Subscriptions, Rooms } from '../../../models/server';
+import { Invites } from '../../../models/server/raw';
import { settings } from '../../../settings';
import { getURL } from '../../../utils/lib/getURL';
import { roomTypes, RoomMemberActions } from '../../../utils/server';
@@ -23,7 +24,7 @@ function getInviteUrl(invite) {
const possibleDays = [0, 1, 7, 15, 30];
const possibleUses = [0, 1, 5, 10, 25, 50, 100];
-export const findOrCreateInvite = (userId, invite) => {
+export const findOrCreateInvite = async (userId, invite) => {
if (!userId || !invite) {
return false;
}
@@ -57,7 +58,7 @@ export const findOrCreateInvite = (userId, invite) => {
}
// Before anything, let's check if there's an existing invite with the same settings for the same channel and user and that has not yet expired.
- const existing = Invites.findOneByUserRoomMaxUsesAndExpiration(userId, invite.rid, maxUses, days);
+ const existing = await Invites.findOneByUserRoomMaxUsesAndExpiration(userId, invite.rid, maxUses, days);
// If an existing invite was found, return it's _id instead of creating a new one.
if (existing) {
@@ -86,7 +87,7 @@ export const findOrCreateInvite = (userId, invite) => {
uses: 0,
};
- Invites.create(createInvite);
+ await Invites.insertOne(createInvite);
Notifications.notifyUser(userId, 'updateInvites', { invite: createInvite });
createInvite.url = getInviteUrl(createInvite);
diff --git a/app/invites/server/functions/listInvites.js b/app/invites/server/functions/listInvites.js
index 476a5f729e09..10d67435237d 100644
--- a/app/invites/server/functions/listInvites.js
+++ b/app/invites/server/functions/listInvites.js
@@ -1,9 +1,9 @@
import { Meteor } from 'meteor/meteor';
-import { hasPermission } from '../../../authorization';
-import { Invites } from '../../../models';
+import { hasPermission } from '../../../authorization/server';
+import { Invites } from '../../../models/server/raw';
-export const listInvites = (userId) => {
+export const listInvites = async (userId) => {
if (!userId) {
throw new Meteor.Error('error-invalid-user', 'Invalid user', { method: 'listInvites' });
}
@@ -12,5 +12,5 @@ export const listInvites = (userId) => {
throw new Meteor.Error('not_authorized');
}
- return Invites.find({}).fetch();
+ return Invites.find({}).toArray();
};
diff --git a/app/invites/server/functions/removeInvite.js b/app/invites/server/functions/removeInvite.js
index eadbe67966b3..0ea066a8e287 100644
--- a/app/invites/server/functions/removeInvite.js
+++ b/app/invites/server/functions/removeInvite.js
@@ -1,9 +1,9 @@
import { Meteor } from 'meteor/meteor';
import { hasPermission } from '../../../authorization';
-import Invites from '../../../models/server/models/Invites';
+import { Invites } from '../../../models/server/raw';
-export const removeInvite = (userId, invite) => {
+export const removeInvite = async (userId, invite) => {
if (!userId || !invite) {
return false;
}
@@ -17,13 +17,13 @@ export const removeInvite = (userId, invite) => {
}
// Before anything, let's check if there's an existing invite
- const existing = Invites.findOneById(invite._id);
+ const existing = await Invites.findOneById(invite._id);
if (!existing) {
throw new Meteor.Error('invalid-invitation-id', 'Invalid Invitation _id', { method: 'removeInvite' });
}
- Invites.removeById(invite._id);
+ await Invites.removeById(invite._id);
return true;
};
diff --git a/app/invites/server/functions/useInviteToken.js b/app/invites/server/functions/useInviteToken.js
index 3cf638fd3e94..6fcef0a40788 100644
--- a/app/invites/server/functions/useInviteToken.js
+++ b/app/invites/server/functions/useInviteToken.js
@@ -1,11 +1,12 @@
import { Meteor } from 'meteor/meteor';
-import { Invites, Users, Subscriptions } from '../../../models/server';
+import { Users, Subscriptions } from '../../../models/server';
+import { Invites } from '../../../models/server/raw';
import { validateInviteToken } from './validateInviteToken';
import { addUserToRoom } from '../../../lib/server/functions/addUserToRoom';
import { roomTypes, RoomMemberActions } from '../../../utils/server';
-export const useInviteToken = (userId, token) => {
+export const useInviteToken = async (userId, token) => {
if (!userId) {
throw new Meteor.Error('error-invalid-user', 'The user is invalid', { method: 'useInviteToken', field: 'userId' });
}
@@ -14,7 +15,7 @@ export const useInviteToken = (userId, token) => {
throw new Meteor.Error('error-invalid-token', 'The invite token is invalid.', { method: 'useInviteToken', field: 'token' });
}
- const { inviteData, room } = validateInviteToken(token);
+ const { inviteData, room } = await validateInviteToken(token);
if (!roomTypes.getConfig(room.t).allowMemberAction(room, RoomMemberActions.INVITE)) {
throw new Meteor.Error('error-room-type-not-allowed', 'Can\'t join room of this type via invite', { method: 'useInviteToken', field: 'token' });
@@ -25,7 +26,7 @@ export const useInviteToken = (userId, token) => {
const subscription = Subscriptions.findOneByRoomIdAndUserId(room._id, user._id, { fields: { _id: 1 } });
if (!subscription) {
- Invites.increaseUsageById(inviteData._id);
+ await Invites.increaseUsageById(inviteData._id);
}
// If the user already has an username, then join the invite room,
diff --git a/app/invites/server/functions/validateInviteToken.js b/app/invites/server/functions/validateInviteToken.js
index dda8add8b612..81febb439442 100644
--- a/app/invites/server/functions/validateInviteToken.js
+++ b/app/invites/server/functions/validateInviteToken.js
@@ -1,13 +1,14 @@
import { Meteor } from 'meteor/meteor';
-import { Invites, Rooms } from '../../../models';
+import { Rooms } from '../../../models';
+import { Invites } from '../../../models/server/raw';
-export const validateInviteToken = (token) => {
+export const validateInviteToken = async (token) => {
if (!token || typeof token !== 'string') {
throw new Meteor.Error('error-invalid-token', 'The invite token is invalid.', { method: 'validateInviteToken', field: 'token' });
}
- const inviteData = Invites.findOneById(token);
+ const inviteData = await Invites.findOneById(token);
if (!inviteData) {
throw new Meteor.Error('error-invalid-token', 'The invite token is invalid.', { method: 'validateInviteToken', field: 'token' });
}
diff --git a/app/lib/server/functions/addOAuthService.ts b/app/lib/server/functions/addOAuthService.ts
index a41bb2ee174e..1a2a220fcddc 100644
--- a/app/lib/server/functions/addOAuthService.ts
+++ b/app/lib/server/functions/addOAuthService.ts
@@ -57,6 +57,18 @@ export function addOAuthService(name: string, values: { [k: string]: string | bo
enterprise: true,
invalidValue: false,
modules: ['oauth-enterprise'] });
+ settingsRegistry.add(`Accounts_OAuth_Custom-${ name }-roles_to_sync` , values.rolesToSync || '', { type: 'string',
+ group: 'OAuth',
+ section: `Custom OAuth: ${ name }`,
+ i18nLabel: 'Accounts_OAuth_Custom_Roles_To_Sync',
+ i18nDescription: 'Accounts_OAuth_Custom_Roles_To_Sync_Description',
+ enterprise: true,
+ enableQuery: {
+ _id: `Accounts_OAuth_Custom-${ name }-merge_roles`,
+ value: true,
+ },
+ invalidValue: '',
+ modules: ['oauth-enterprise'] });
settingsRegistry.add(`Accounts_OAuth_Custom-${ name }-merge_users`, values.mergeUsers || false, { type: 'boolean', group: 'OAuth', section: `Custom OAuth: ${ name }`, i18nLabel: 'Accounts_OAuth_Custom_Merge_Users', persistent: true });
settingsRegistry.add(`Accounts_OAuth_Custom-${ name }-show_button` , values.showButton || true , { type: 'boolean', group: 'OAuth', section: `Custom OAuth: ${ name }`, i18nLabel: 'Accounts_OAuth_Custom_Show_Button_On_Login_Page', persistent: true });
settingsRegistry.add(`Accounts_OAuth_Custom-${ name }-groups_channel_map` , values.channelsMap || '{\n\t"rocket-admin": "admin",\n\t"tech-support": "support"\n}' , { type: 'code' , multiline: true, code: 'application/json', group: 'OAuth', section: `Custom OAuth: ${ name }`, i18nLabel: 'Accounts_OAuth_Custom_Channel_Map', persistent: true });
diff --git a/app/lib/server/functions/closeOmnichannelConversations.ts b/app/lib/server/functions/closeOmnichannelConversations.ts
index 46be29d8a984..c5cff5081f35 100644
--- a/app/lib/server/functions/closeOmnichannelConversations.ts
+++ b/app/lib/server/functions/closeOmnichannelConversations.ts
@@ -12,8 +12,9 @@ type SubscribedRooms = {
export const closeOmnichannelConversations = (user: IUser, subscribedRooms: SubscribedRooms[]): void => {
const roomsInfo = LivechatRooms.findByIds(subscribedRooms.map(({ rid }) => rid));
- const language = settings.get('Language') || 'en';
- roomsInfo.map((room: any) =>
- Livechat.closeRoom({ user, visitor: {}, room, comment: TAPi18n.__('Agent_deactivated', { lng: language }) }),
- );
+ const language = settings.get('Language') || 'en';
+ const comment = TAPi18n.__('Agent_deactivated', { lng: language });
+ roomsInfo.forEach((room: any) => {
+ Livechat.closeRoom({ user, visitor: {}, room, comment });
+ });
};
diff --git a/app/lib/server/functions/deleteMessage.ts b/app/lib/server/functions/deleteMessage.ts
index 8f698842e205..96e563b1a464 100644
--- a/app/lib/server/functions/deleteMessage.ts
+++ b/app/lib/server/functions/deleteMessage.ts
@@ -2,14 +2,15 @@ import { Meteor } from 'meteor/meteor';
import { FileUpload } from '../../../file-upload/server';
import { settings } from '../../../settings/server';
-import { Messages, Uploads, Rooms } from '../../../models/server';
+import { Messages, Rooms } from '../../../models/server';
+import { Uploads } from '../../../models/server/raw';
import { Notifications } from '../../../notifications/server';
import { callbacks } from '../../../callbacks/server';
import { Apps } from '../../../apps/server';
import { IMessage } from '../../../../definition/IMessage';
import { IUser } from '../../../../definition/IUser';
-export const deleteMessage = function(message: IMessage, user: IUser): void {
+export const deleteMessage = async function(message: IMessage, user: IUser): Promise {
const deletedMsg = Messages.findOneById(message._id);
const isThread = deletedMsg.tcount > 0;
const keepHistory = settings.get('Message_KeepHistory') || isThread;
@@ -36,9 +37,9 @@ export const deleteMessage = function(message: IMessage, user: IUser): void {
Messages.setHiddenById(message._id, true);
}
- files.forEach((file) => {
- file?._id && Uploads.update(file._id, { $set: { _hidden: true } });
- });
+ for await (const file of files) {
+ file?._id && await Uploads.update({ _id: file._id }, { $set: { _hidden: true } });
+ }
} else {
if (!showDeletedStatus) {
Messages.removeById(message._id);
diff --git a/app/lib/server/functions/deleteUser.js b/app/lib/server/functions/deleteUser.js
index 680517db5405..4193774e1136 100644
--- a/app/lib/server/functions/deleteUser.js
+++ b/app/lib/server/functions/deleteUser.js
@@ -2,7 +2,8 @@ import { Meteor } from 'meteor/meteor';
import { TAPi18n } from 'meteor/rocketchat:tap-i18n';
import { FileUpload } from '../../../file-upload/server';
-import { Users, Subscriptions, Messages, Rooms, Integrations, FederationServers } from '../../../models/server';
+import { Users, Subscriptions, Messages, Rooms } from '../../../models/server';
+import { FederationServers, Integrations } from '../../../models/server/raw';
import { settings } from '../../../settings/server';
import { updateGroupDMsName } from './updateGroupDMsName';
import { relinquishRoomOwnerships } from './relinquishRoomOwnerships';
@@ -10,7 +11,7 @@ import { getSubscribedRoomsForUserWithDetails, shouldRemoveOrChangeOwner } from
import { getUserSingleOwnedRooms } from './getUserSingleOwnedRooms';
import { api } from '../../../../server/sdk/api';
-export const deleteUser = function(userId, confirmRelinquish = false) {
+export async function deleteUser(userId, confirmRelinquish = false) {
const user = Users.findOneById(userId, {
fields: { username: 1, avatarOrigin: 1, federation: 1 },
});
@@ -36,7 +37,7 @@ export const deleteUser = function(userId, confirmRelinquish = false) {
// Users without username can't do anything, so there is nothing to remove
if (user.username != null) {
- relinquishRoomOwnerships(userId, subscribedRooms);
+ await relinquishRoomOwnerships(userId, subscribedRooms);
const messageErasureType = settings.get('Message_ErasureType');
switch (messageErasureType) {
@@ -64,7 +65,7 @@ export const deleteUser = function(userId, confirmRelinquish = false) {
FileUpload.getStore('Avatars').deleteByName(user.username);
}
- Integrations.disableByUserId(userId); // Disables all the integrations which rely on the user being deleted.
+ await Integrations.disableByUserId(userId); // Disables all the integrations which rely on the user being deleted.
api.broadcast('user.deleted', user);
}
@@ -75,5 +76,5 @@ export const deleteUser = function(userId, confirmRelinquish = false) {
updateGroupDMsName(user);
// Refresh the servers list
- FederationServers.refreshServers();
-};
+ await FederationServers.refreshServers();
+}
diff --git a/app/lib/server/functions/relinquishRoomOwnerships.js b/app/lib/server/functions/relinquishRoomOwnerships.js
index 7c56e3bc05a5..f5c403f1b2b1 100644
--- a/app/lib/server/functions/relinquishRoomOwnerships.js
+++ b/app/lib/server/functions/relinquishRoomOwnerships.js
@@ -1,5 +1,6 @@
import { FileUpload } from '../../../file-upload/server';
-import { Subscriptions, Messages, Rooms, Roles } from '../../../models/server';
+import { Subscriptions, Messages, Rooms } from '../../../models/server';
+import { Roles } from '../../../models/server/raw';
const bulkRoomCleanUp = (rids) => {
// no bulk deletion for files
@@ -12,11 +13,14 @@ const bulkRoomCleanUp = (rids) => {
]));
};
-export const relinquishRoomOwnerships = function(userId, subscribedRooms, removeDirectMessages = true) {
+export const relinquishRoomOwnerships = async function(userId, subscribedRooms, removeDirectMessages = true) {
// change owners
- subscribedRooms
- .filter(({ shouldChangeOwner }) => shouldChangeOwner)
- .forEach(({ newOwner, rid }) => Roles.addUserRoles(newOwner, ['owner'], rid));
+ const changeOwner = subscribedRooms
+ .filter(({ shouldChangeOwner }) => shouldChangeOwner);
+
+ for await (const { newOwner, rid } of changeOwner) {
+ await Roles.addUserRoles(newOwner, ['owner'], rid);
+ }
const roomIdsToRemove = subscribedRooms.filter(({ shouldBeRemoved }) => shouldBeRemoved).map(({ rid }) => rid);
diff --git a/app/lib/server/functions/setRoomAvatar.js b/app/lib/server/functions/setRoomAvatar.js
index 9b0ea487c758..540de2799201 100644
--- a/app/lib/server/functions/setRoomAvatar.js
+++ b/app/lib/server/functions/setRoomAvatar.js
@@ -2,13 +2,14 @@ import { Meteor } from 'meteor/meteor';
import { RocketChatFile } from '../../../file';
import { FileUpload } from '../../../file-upload';
-import { Rooms, Avatars, Messages } from '../../../models/server';
+import { Rooms, Messages } from '../../../models/server';
+import { Avatars } from '../../../models/server/raw';
import { api } from '../../../../server/sdk/api';
-export const setRoomAvatar = function(rid, dataURI, user) {
+export const setRoomAvatar = async function(rid, dataURI, user) {
const fileStore = FileUpload.getStore('Avatars');
- const current = Avatars.findOneByRoomId(rid);
+ const current = await Avatars.findOneByRoomId(rid);
if (!dataURI) {
fileStore.deleteByRoomId(rid);
diff --git a/app/lib/server/functions/setUserActiveStatus.js b/app/lib/server/functions/setUserActiveStatus.js
index 8089fbf7d1cf..77a137695fdd 100644
--- a/app/lib/server/functions/setUserActiveStatus.js
+++ b/app/lib/server/functions/setUserActiveStatus.js
@@ -63,7 +63,7 @@ export function setUserActiveStatus(userId, active, confirmRelinquish = false) {
}
closeOmnichannelConversations(user, livechatSubscribedRooms);
- relinquishRoomOwnerships(user, chatSubscribedRooms, false);
+ Promise.await(relinquishRoomOwnerships(user, chatSubscribedRooms, false));
}
if (active && !user.active) {
diff --git a/app/lib/server/functions/setUsername.js b/app/lib/server/functions/setUsername.js
index 8795fb6b01fc..97a05291f5d1 100644
--- a/app/lib/server/functions/setUsername.js
+++ b/app/lib/server/functions/setUsername.js
@@ -3,7 +3,8 @@ import s from 'underscore.string';
import { Accounts } from 'meteor/accounts-base';
import { settings } from '../../../settings';
-import { Users, Invites } from '../../../models/server';
+import { Users } from '../../../models/server';
+import { Invites } from '../../../models/server/raw';
import { hasPermission } from '../../../authorization';
import { RateLimiter } from '../lib';
import { addUserToRoom } from './addUserToRoom';
@@ -70,7 +71,7 @@ export const _setUsername = function(userId, u, fullUser) {
// If it's the first username and the user has an invite Token, then join the invite room
if (!previousUsername && user.inviteToken) {
- const inviteData = Invites.findOneById(user.inviteToken);
+ const inviteData = Promise.await(Invites.findOneById(user.inviteToken));
if (inviteData && inviteData.rid) {
addUserToRoom(inviteData.rid, user);
}
diff --git a/app/lib/server/lib/getRoomRoles.js b/app/lib/server/lib/getRoomRoles.js
index 9c3718628782..6ed6527c5368 100644
--- a/app/lib/server/lib/getRoomRoles.js
+++ b/app/lib/server/lib/getRoomRoles.js
@@ -1,7 +1,8 @@
import _ from 'underscore';
import { settings } from '../../../settings';
-import { Subscriptions, Users, Roles } from '../../../models';
+import { Subscriptions, Users } from '../../../models';
+import { Roles } from '../../../models/server/raw';
export function getRoomRoles(rid) {
const options = {
@@ -17,7 +18,7 @@ export function getRoomRoles(rid) {
const UI_Use_Real_Name = settings.get('UI_Use_Real_Name') === true;
- const roles = Roles.find({ scope: 'Subscriptions', description: { $exists: 1, $ne: '' } }).fetch();
+ const roles = Promise.await(Roles.find({ scope: 'Subscriptions', description: { $exists: 1, $ne: '' } }).toArray());
const subscriptions = Subscriptions.findByRoomIdAndRoles(rid, _.pluck(roles, '_id'), options).fetch();
if (!UI_Use_Real_Name) {
diff --git a/app/lib/server/methods/deleteMessage.js b/app/lib/server/methods/deleteMessage.js
index 086b9caee7f9..8be7da4e5e45 100644
--- a/app/lib/server/methods/deleteMessage.js
+++ b/app/lib/server/methods/deleteMessage.js
@@ -6,7 +6,7 @@ import { Messages } from '../../../models';
import { deleteMessage } from '../functions';
Meteor.methods({
- deleteMessage(message) {
+ async deleteMessage(message) {
check(message, Match.ObjectIncluding({
_id: String,
}));
diff --git a/app/lib/server/methods/deleteUserOwnAccount.js b/app/lib/server/methods/deleteUserOwnAccount.js
index 1ff7494a8751..2d7f269b08f7 100644
--- a/app/lib/server/methods/deleteUserOwnAccount.js
+++ b/app/lib/server/methods/deleteUserOwnAccount.js
@@ -9,7 +9,7 @@ import { Users } from '../../../models';
import { deleteUser } from '../functions';
Meteor.methods({
- deleteUserOwnAccount(password, confirmRelinquish) {
+ async deleteUserOwnAccount(password, confirmRelinquish) {
check(password, String);
if (!Meteor.userId()) {
@@ -39,7 +39,7 @@ Meteor.methods({
throw new Meteor.Error('error-invalid-username', 'Invalid username', { method: 'deleteUserOwnAccount' });
}
- deleteUser(userId, confirmRelinquish);
+ await deleteUser(userId, confirmRelinquish);
return true;
},
diff --git a/app/lib/server/methods/leaveRoom.js b/app/lib/server/methods/leaveRoom.ts
similarity index 76%
rename from app/lib/server/methods/leaveRoom.js
rename to app/lib/server/methods/leaveRoom.ts
index 561d7bdb548a..cce12a25b8f8 100644
--- a/app/lib/server/methods/leaveRoom.js
+++ b/app/lib/server/methods/leaveRoom.ts
@@ -1,13 +1,14 @@
import { Meteor } from 'meteor/meteor';
import { check } from 'meteor/check';
-import { hasPermission, hasRole, getUsersInRole } from '../../../authorization';
-import { Subscriptions, Rooms } from '../../../models';
+import { hasPermission, hasRole } from '../../../authorization/server';
+import { Subscriptions, Rooms } from '../../../models/server';
import { removeUserFromRoom } from '../functions';
import { roomTypes, RoomMemberActions } from '../../../utils/server';
+import { Roles } from '../../../models/server/raw';
Meteor.methods({
- leaveRoom(rid) {
+ async leaveRoom(rid) {
check(rid, String);
if (!Meteor.userId()) {
@@ -17,7 +18,7 @@ Meteor.methods({
const room = Rooms.findOneById(rid);
const user = Meteor.user();
- if (!roomTypes.getConfig(room.t).allowMemberAction(room, RoomMemberActions.LEAVE)) {
+ if (!user || !roomTypes.getConfig(room.t).allowMemberAction(room, RoomMemberActions.LEAVE)) {
throw new Meteor.Error('error-not-allowed', 'Not allowed', { method: 'leaveRoom' });
}
@@ -32,7 +33,8 @@ Meteor.methods({
// If user is room owner, check if there are other owners. If there isn't anyone else, warn user to set a new owner.
if (hasRole(user._id, 'owner', room._id)) {
- const numOwners = getUsersInRole('owner', room._id).count();
+ const cursor = await Roles.findUsersInRole('owner', room._id);
+ const numOwners = Promise.await(cursor.count());
if (numOwners === 1) {
throw new Meteor.Error('error-you-are-last-owner', 'You are the last owner. Please set new owner before leaving the room.', { method: 'leaveRoom' });
}
diff --git a/app/lib/server/methods/refreshOAuthService.js b/app/lib/server/methods/refreshOAuthService.ts
similarity index 66%
rename from app/lib/server/methods/refreshOAuthService.js
rename to app/lib/server/methods/refreshOAuthService.ts
index e0ef565cb45e..14dbeaa1fcd6 100644
--- a/app/lib/server/methods/refreshOAuthService.js
+++ b/app/lib/server/methods/refreshOAuthService.ts
@@ -1,11 +1,11 @@
import { Meteor } from 'meteor/meteor';
import { ServiceConfiguration } from 'meteor/service-configuration';
-import { hasPermission } from '../../../authorization';
-import { Settings } from '../../../models';
+import { hasPermission } from '../../../authorization/server';
+import { Settings } from '../../../models/server/raw';
Meteor.methods({
- refreshOAuthService() {
+ async refreshOAuthService() {
if (!Meteor.userId()) {
throw new Meteor.Error('error-invalid-user', 'Invalid user', { method: 'refreshOAuthService' });
}
@@ -16,6 +16,6 @@ Meteor.methods({
ServiceConfiguration.configurations.remove({});
- Settings.update({ _id: /^(Accounts_OAuth_|SAML_|CAS_|Blockstack_).+/ }, { $set: { _updatedAt: new Date() } }, { multi: true });
+ await Settings.update({ _id: /^(Accounts_OAuth_|SAML_|CAS_|Blockstack_).+/ }, { $set: { _updatedAt: new Date() } }, { multi: true });
},
});
diff --git a/app/lib/server/methods/removeOAuthService.js b/app/lib/server/methods/removeOAuthService.js
deleted file mode 100644
index 5c271f3e75f0..000000000000
--- a/app/lib/server/methods/removeOAuthService.js
+++ /dev/null
@@ -1,51 +0,0 @@
-import { capitalize } from '@rocket.chat/string-helpers';
-import { Meteor } from 'meteor/meteor';
-import { check } from 'meteor/check';
-
-import { hasPermission } from '../../../authorization';
-import { Settings } from '../../../models/server';
-
-Meteor.methods({
- removeOAuthService(name) {
- check(name, String);
-
- if (!Meteor.userId()) {
- throw new Meteor.Error('error-invalid-user', 'Invalid user', { method: 'removeOAuthService' });
- }
-
- if (hasPermission(Meteor.userId(), 'add-oauth-service') !== true) {
- throw new Meteor.Error('error-not-allowed', 'Not allowed', { method: 'removeOAuthService' });
- }
-
- name = name.toLowerCase().replace(/[^a-z0-9_]/g, '');
- name = capitalize(name);
- Settings.removeById(`Accounts_OAuth_Custom-${ name }`);
- Settings.removeById(`Accounts_OAuth_Custom-${ name }-url`);
- Settings.removeById(`Accounts_OAuth_Custom-${ name }-token_path`);
- Settings.removeById(`Accounts_OAuth_Custom-${ name }-identity_path`);
- Settings.removeById(`Accounts_OAuth_Custom-${ name }-authorize_path`);
- Settings.removeById(`Accounts_OAuth_Custom-${ name }-scope`);
- Settings.removeById(`Accounts_OAuth_Custom-${ name }-access_token_param`);
- Settings.removeById(`Accounts_OAuth_Custom-${ name }-token_sent_via`);
- Settings.removeById(`Accounts_OAuth_Custom-${ name }-identity_token_sent_via`);
- Settings.removeById(`Accounts_OAuth_Custom-${ name }-id`);
- Settings.removeById(`Accounts_OAuth_Custom-${ name }-secret`);
- Settings.removeById(`Accounts_OAuth_Custom-${ name }-button_label_text`);
- Settings.removeById(`Accounts_OAuth_Custom-${ name }-button_label_color`);
- Settings.removeById(`Accounts_OAuth_Custom-${ name }-button_color`);
- Settings.removeById(`Accounts_OAuth_Custom-${ name }-login_style`);
- Settings.removeById(`Accounts_OAuth_Custom-${ name }-key_field`);
- Settings.removeById(`Accounts_OAuth_Custom-${ name }-username_field`);
- Settings.removeById(`Accounts_OAuth_Custom-${ name }-email_field`);
- Settings.removeById(`Accounts_OAuth_Custom-${ name }-name_field`);
- Settings.removeById(`Accounts_OAuth_Custom-${ name }-avatar_field`);
- Settings.removeById(`Accounts_OAuth_Custom-${ name }-roles_claim`);
- Settings.removeById(`Accounts_OAuth_Custom-${ name }-merge_roles`);
- Settings.removeById(`Accounts_OAuth_Custom-${ name }-merge_users`);
- Settings.removeById(`Accounts_OAuth_Custom-${ name }-show_button`);
- Settings.removeById(`Accounts_OAuth_Custom-${ name }-groups_claim`);
- Settings.removeById(`Accounts_OAuth_Custom-${ name }-channels_admin`);
- Settings.removeById(`Accounts_OAuth_Custom-${ name }-map_channels`);
- Settings.removeById(`Accounts_OAuth_Custom-${ name }-groups_channel_map`);
- },
-});
diff --git a/app/lib/server/methods/removeOAuthService.ts b/app/lib/server/methods/removeOAuthService.ts
new file mode 100644
index 000000000000..4aecbcdc53df
--- /dev/null
+++ b/app/lib/server/methods/removeOAuthService.ts
@@ -0,0 +1,55 @@
+import { capitalize } from '@rocket.chat/string-helpers';
+import { Meteor } from 'meteor/meteor';
+import { check } from 'meteor/check';
+
+import { hasPermission } from '../../../authorization/server';
+import { Settings } from '../../../models/server/raw';
+
+
+Meteor.methods({
+ async removeOAuthService(name) {
+ check(name, String);
+
+ if (!Meteor.userId()) {
+ throw new Meteor.Error('error-invalid-user', 'Invalid user', { method: 'removeOAuthService' });
+ }
+
+ if (hasPermission(Meteor.userId(), 'add-oauth-service') !== true) {
+ throw new Meteor.Error('error-not-allowed', 'Not allowed', { method: 'removeOAuthService' });
+ }
+
+ name = name.toLowerCase().replace(/[^a-z0-9_]/g, '');
+ name = capitalize(name);
+ await Promise.all([
+ Settings.removeById(`Accounts_OAuth_Custom-${ name }`),
+ Settings.removeById(`Accounts_OAuth_Custom-${ name }-url`),
+ Settings.removeById(`Accounts_OAuth_Custom-${ name }-token_path`),
+ Settings.removeById(`Accounts_OAuth_Custom-${ name }-identity_path`),
+ Settings.removeById(`Accounts_OAuth_Custom-${ name }-authorize_path`),
+ Settings.removeById(`Accounts_OAuth_Custom-${ name }-scope`),
+ Settings.removeById(`Accounts_OAuth_Custom-${ name }-access_token_param`),
+ Settings.removeById(`Accounts_OAuth_Custom-${ name }-token_sent_via`),
+ Settings.removeById(`Accounts_OAuth_Custom-${ name }-identity_token_sent_via`),
+ Settings.removeById(`Accounts_OAuth_Custom-${ name }-id`),
+ Settings.removeById(`Accounts_OAuth_Custom-${ name }-secret`),
+ Settings.removeById(`Accounts_OAuth_Custom-${ name }-button_label_text`),
+ Settings.removeById(`Accounts_OAuth_Custom-${ name }-button_label_color`),
+ Settings.removeById(`Accounts_OAuth_Custom-${ name }-button_color`),
+ Settings.removeById(`Accounts_OAuth_Custom-${ name }-login_style`),
+ Settings.removeById(`Accounts_OAuth_Custom-${ name }-key_field`),
+ Settings.removeById(`Accounts_OAuth_Custom-${ name }-username_field`),
+ Settings.removeById(`Accounts_OAuth_Custom-${ name }-email_field`),
+ Settings.removeById(`Accounts_OAuth_Custom-${ name }-name_field`),
+ Settings.removeById(`Accounts_OAuth_Custom-${ name }-avatar_field`),
+ Settings.removeById(`Accounts_OAuth_Custom-${ name }-roles_claim`),
+ Settings.removeById(`Accounts_OAuth_Custom-${ name }-merge_roles`),
+ Settings.removeById(`Accounts_OAuth_Custom-${ name }-roles_to_sync`),
+ Settings.removeById(`Accounts_OAuth_Custom-${ name }-merge_users`),
+ Settings.removeById(`Accounts_OAuth_Custom-${ name }-show_button`),
+ Settings.removeById(`Accounts_OAuth_Custom-${ name }-groups_claim`),
+ Settings.removeById(`Accounts_OAuth_Custom-${ name }-channels_admin`),
+ Settings.removeById(`Accounts_OAuth_Custom-${ name }-map_channels`),
+ Settings.removeById(`Accounts_OAuth_Custom-${ name }-groups_channel_map`),
+ ]);
+ },
+});
diff --git a/app/lib/server/methods/saveSetting.js b/app/lib/server/methods/saveSetting.js
index b375b1ad5952..993915db53f6 100644
--- a/app/lib/server/methods/saveSetting.js
+++ b/app/lib/server/methods/saveSetting.js
@@ -3,11 +3,11 @@ import { Match, check } from 'meteor/check';
import { hasPermission, hasAllPermission } from '../../../authorization/server';
import { getSettingPermissionId } from '../../../authorization/lib';
-import { Settings } from '../../../models';
import { twoFactorRequired } from '../../../2fa/server/twoFactorRequired';
+import { Settings } from '../../../models/server/raw';
Meteor.methods({
- saveSetting: twoFactorRequired(function(_id, value, editor) {
+ saveSetting: twoFactorRequired(async function(_id, value, editor) {
const uid = Meteor.userId();
if (!uid) {
throw new Meteor.Error('error-action-not-allowed', 'Editing settings is not allowed', {
@@ -26,7 +26,7 @@ Meteor.methods({
// Verify the _id passed in is a string.
check(_id, String);
- const setting = Settings.db.findOneById(_id);
+ const setting = await Settings.findOneById(_id);
// Verify the value is what it should be
switch (setting.type) {
@@ -44,7 +44,7 @@ Meteor.methods({
break;
}
- Settings.updateValueAndEditorById(_id, value, editor);
+ await Settings.updateValueAndEditorById(_id, value, editor);
return true;
}),
});
diff --git a/app/lib/server/methods/saveSettings.js b/app/lib/server/methods/saveSettings.js
index 6b99b3c7665c..6da862bd9aa5 100644
--- a/app/lib/server/methods/saveSettings.js
+++ b/app/lib/server/methods/saveSettings.js
@@ -1,13 +1,13 @@
import { Meteor } from 'meteor/meteor';
import { Match, check } from 'meteor/check';
-import { hasPermission } from '../../../authorization';
-import { Settings } from '../../../models';
+import { hasPermission } from '../../../authorization/server';
import { getSettingPermissionId } from '../../../authorization/lib';
import { twoFactorRequired } from '../../../2fa/server/twoFactorRequired';
+import { Settings } from '../../../models/server/raw';
Meteor.methods({
- saveSettings: twoFactorRequired(function(params = []) {
+ saveSettings: twoFactorRequired(async function(params = []) {
const uid = Meteor.userId();
const settingsNotAllowed = [];
if (uid === null) {
@@ -18,16 +18,16 @@ Meteor.methods({
const editPrivilegedSetting = hasPermission(uid, 'edit-privileged-setting');
const manageSelectedSettings = hasPermission(uid, 'manage-selected-settings');
- params.forEach(({ _id, value }) => {
+ await Promise.all(params.map(async ({ _id, value }) => {
// Verify the _id passed in is a string.
check(_id, String);
if (!editPrivilegedSetting && !(manageSelectedSettings && hasPermission(uid, getSettingPermissionId(_id)))) {
return settingsNotAllowed.push(_id);
}
- const setting = Settings.db.findOneById(_id);
+ const setting = await Settings.findOneById(_id);
// Verify the value is what it should be
- switch (setting.type) {
+ switch (setting?.type) {
case 'roomPick':
check(value, Match.OneOf([Object], ''));
break;
@@ -44,7 +44,7 @@ Meteor.methods({
check(value, String);
break;
}
- });
+ }));
if (settingsNotAllowed.length) {
throw new Meteor.Error('error-action-not-allowed', 'Editing settings is not allowed', {
@@ -53,8 +53,8 @@ Meteor.methods({
});
}
- params.forEach(({ _id, value, editor }) => Settings.updateValueById(_id, value, editor));
+ await Promise.all(params.map(({ _id, value, editor }) => Settings.updateValueById(_id, value, editor)));
return true;
- }),
+ }, {}),
});
diff --git a/app/lib/server/startup/oAuthServicesUpdate.js b/app/lib/server/startup/oAuthServicesUpdate.js
index f3206824e277..c0a4d6f46b58 100644
--- a/app/lib/server/startup/oAuthServicesUpdate.js
+++ b/app/lib/server/startup/oAuthServicesUpdate.js
@@ -55,6 +55,7 @@ function _OAuthServicesUpdate() {
data.mergeUsers = settings.get(`${ key }-merge_users`);
data.mapChannels = settings.get(`${ key }-map_channels`);
data.mergeRoles = settings.get(`${ key }-merge_roles`);
+ data.rolesToSync = settings.get(`${ key }-roles_to_sync`);
data.showButton = settings.get(`${ key }-show_button`);
new CustomOAuth(serviceName.toLowerCase(), {
@@ -78,6 +79,7 @@ function _OAuthServicesUpdate() {
channelsAdmin: data.channelsAdmin,
mergeUsers: data.mergeUsers,
mergeRoles: data.mergeRoles,
+ rolesToSync: data.rolesToSync,
accessTokenParam: data.accessTokenParam,
showButton: data.showButton,
});
@@ -184,6 +186,7 @@ function customOAuthServicesInit() {
mergeUsers: process.env[`${ serviceKey }_merge_users`] === 'true',
mapChannels: process.env[`${ serviceKey }_map_channels`],
mergeRoles: process.env[`${ serviceKey }_merge_roles`] === 'true',
+ rolesToSync: process.env[`${ serviceKey }_roles_to_sync`],
showButton: process.env[`${ serviceKey }_show_button`] === 'true',
avatarField: process.env[`${ serviceKey }_avatar_field`],
};
diff --git a/app/lib/server/startup/rateLimiter.js b/app/lib/server/startup/rateLimiter.js
index 464404b88048..e10921f3e8de 100644
--- a/app/lib/server/startup/rateLimiter.js
+++ b/app/lib/server/startup/rateLimiter.js
@@ -108,10 +108,9 @@ const checkNameForStream = (name) => name && !names.has(name) && name.startsWith
const ruleIds = {};
-const callback = (message, name) => (reply, input) => {
+const callback = (msg, name) => (reply, input) => {
if (reply.allowed === false) {
- logger.info('DDP RATE LIMIT:', message);
- logger.info({ ...reply, ...input });
+ logger.info({ msg, reply, input });
metrics.ddpRateLimitExceeded.inc({
limit_name: name,
user_id: input.userId,
diff --git a/app/lib/server/startup/settings.ts b/app/lib/server/startup/settings.ts
index b8c3309b5cf2..1562634a24ee 100644
--- a/app/lib/server/startup/settings.ts
+++ b/app/lib/server/startup/settings.ts
@@ -1675,10 +1675,6 @@ settingsRegistry.addGroup('Setup_Wizard', function() {
key: 'aerospaceDefense',
i18nLabel: 'Aerospace_and_Defense',
},
- {
- key: 'blockchain',
- i18nLabel: 'Blockchain',
- },
{
key: 'consulting',
i18nLabel: 'Consulting',
@@ -2977,7 +2973,7 @@ settingsRegistry.addGroup('Setup_Wizard', function() {
});
settingsRegistry.addGroup('Rate Limiter', function() {
- this.section('DDP Rate Limiter', function() {
+ this.section('DDP_Rate_Limiter', function() {
this.add('DDP_Rate_Limit_IP_Enabled', true, { type: 'boolean' });
this.add('DDP_Rate_Limit_IP_Requests_Allowed', 120000, { type: 'int', enableQuery: { _id: 'DDP_Rate_Limit_IP_Enabled', value: true } });
this.add('DDP_Rate_Limit_IP_Interval_Time', 60000, { type: 'int', enableQuery: { _id: 'DDP_Rate_Limit_IP_Enabled', value: true } });
@@ -2999,12 +2995,16 @@ settingsRegistry.addGroup('Rate Limiter', function() {
this.add('DDP_Rate_Limit_Connection_By_Method_Interval_Time', 10000, { type: 'int', enableQuery: { _id: 'DDP_Rate_Limit_Connection_By_Method_Enabled', value: true } });
});
- this.section('API Rate Limiter', function() {
+ this.section('API_Rate_Limiter', function() {
this.add('API_Enable_Rate_Limiter', true, { type: 'boolean' });
this.add('API_Enable_Rate_Limiter_Dev', true, { type: 'boolean', enableQuery: { _id: 'API_Enable_Rate_Limiter', value: true } });
this.add('API_Enable_Rate_Limiter_Limit_Calls_Default', 10, { type: 'int', enableQuery: { _id: 'API_Enable_Rate_Limiter', value: true } });
this.add('API_Enable_Rate_Limiter_Limit_Time_Default', 60000, { type: 'int', enableQuery: { _id: 'API_Enable_Rate_Limiter', value: true } });
});
+
+ this.section('Feature_Limiting', function() {
+ this.add('Rate_Limiter_Limit_RegisterUser', 1, { type: 'int', enableQuery: { _id: 'API_Enable_Rate_Limiter', value: true } });
+ });
});
settingsRegistry.addGroup('Troubleshoot', function() {
diff --git a/app/lib/tests/server.tests.js b/app/lib/tests/server.tests.js
index cc6a4de04b1a..a606cff94901 100644
--- a/app/lib/tests/server.tests.js
+++ b/app/lib/tests/server.tests.js
@@ -1,7 +1,3 @@
-/* eslint-env mocha */
-import 'babel-polyfill';
-import assert from 'assert';
-
import { expect } from 'chai';
import './server.mocks.js';
@@ -41,11 +37,11 @@ describe('PasswordPolicyClass', () => {
describe('Password tests with default options', () => {
it('should allow all passwords', () => {
const passwordPolice = new PasswordPolicyClass({ throwError: false });
- assert.equal(passwordPolice.validate(), false);
- assert.equal(passwordPolice.validate(''), false);
- assert.equal(passwordPolice.validate(' '), false);
- assert.equal(passwordPolice.validate('a'), true);
- assert.equal(passwordPolice.validate('aaaaaaaaa'), true);
+ expect(passwordPolice.validate()).to.be.equal(false);
+ expect(passwordPolice.validate('')).to.be.equal(false);
+ expect(passwordPolice.validate(' ')).to.be.equal(false);
+ expect(passwordPolice.validate('a')).to.be.equal(true);
+ expect(passwordPolice.validate('aaaaaaaaa')).to.be.equal(true);
});
});
});
diff --git a/app/livechat/client/lib/chartHandler.js b/app/livechat/client/lib/chartHandler.js
index 7af6388409b1..f99571f0e66d 100644
--- a/app/livechat/client/lib/chartHandler.js
+++ b/app/livechat/client/lib/chartHandler.js
@@ -194,9 +194,9 @@ export const drawDoughnutChart = async (chart, title, chartContext, dataLabels,
data: dataPoints, // data points corresponding to data labels, x-axis points
backgroundColor: [
'#2de0a5',
- '#ffd21f',
- '#f5455c',
'#cbced1',
+ '#f5455c',
+ '#ffd21f',
],
borderWidth: 0,
}],
diff --git a/app/livechat/client/views/app/tabbar/contactChatHistoryMessages.html b/app/livechat/client/views/app/tabbar/contactChatHistoryMessages.html
index 1b5e7ec2b104..7b83a9db8de7 100644
--- a/app/livechat/client/views/app/tabbar/contactChatHistoryMessages.html
+++ b/app/livechat/client/views/app/tabbar/contactChatHistoryMessages.html
@@ -33,17 +33,25 @@ {{_ "No_results_found"}}
{{else}}
-
+ {{/if}}
{{/if}}
diff --git a/app/livechat/client/views/app/tabbar/contactChatHistoryMessages.js b/app/livechat/client/views/app/tabbar/contactChatHistoryMessages.js
index c73136c07a8f..cb3171f67c65 100644
--- a/app/livechat/client/views/app/tabbar/contactChatHistoryMessages.js
+++ b/app/livechat/client/views/app/tabbar/contactChatHistoryMessages.js
@@ -36,6 +36,12 @@ Template.contactChatHistoryMessages.helpers({
empty() {
return Template.instance().messages.get().length === 0;
},
+ hasError() {
+ return Template.instance().hasError.get();
+ },
+ error() {
+ return Template.instance().error.get();
+ },
});
Template.contactChatHistoryMessages.events({
@@ -72,15 +78,23 @@ Template.contactChatHistoryMessages.onCreated(function() {
this.searchTerm = new ReactiveVar('');
this.isLoading = new ReactiveVar(true);
this.limit = new ReactiveVar(MESSAGES_LIMIT);
+ this.hasError = new ReactiveVar(false);
+ this.error = new ReactiveVar(null);
this.loadMessages = async (url) => {
this.isLoading.set(true);
const offset = this.offset.get();
- const { messages, total } = await APIClient.v1.get(url);
- this.messages.set(offset === 0 ? messages : this.messages.get().concat(messages));
- this.hasMore.set(total > this.messages.get().length);
- this.isLoading.set(false);
+ try {
+ const { messages, total } = await APIClient.v1.get(url);
+ this.messages.set(offset === 0 ? messages : this.messages.get().concat(messages));
+ this.hasMore.set(total > this.messages.get().length);
+ } catch (e) {
+ this.hasError.set(true);
+ this.error.set(e);
+ } finally {
+ this.isLoading.set(false);
+ }
};
this.autorun(() => {
@@ -92,7 +106,7 @@ Template.contactChatHistoryMessages.onCreated(function() {
return this.loadMessages(`chat.search/?roomId=${ this.rid }&searchText=${ searchTerm }&count=${ limit }&offset=${ offset }&sort={"ts": 1}`);
}
- this.loadMessages(`channels.messages/?roomId=${ this.rid }&count=${ limit }&offset=${ offset }&sort={"ts": 1}&query={"$or": [ {"t": {"$exists": false} }, {"t": "livechat-close"} ] }`);
+ this.loadMessages(`livechat/${ this.rid }/messages?count=${ limit }&offset=${ offset }&sort={"ts": 1}`);
});
this.autorun(() => {
diff --git a/app/livechat/imports/server/rest/rooms.js b/app/livechat/imports/server/rest/rooms.js
index d2f4c6e20d63..9680b8baffce 100644
--- a/app/livechat/imports/server/rest/rooms.js
+++ b/app/livechat/imports/server/rest/rooms.js
@@ -21,12 +21,13 @@ API.v1.addRoute('livechat/rooms', { authRequired: true }, {
get() {
const { offset, count } = this.getPaginationItems();
const { sort, fields } = this.parseJsonQuery();
- const { agents, departmentId, open, tags, roomName } = this.requestParams();
+ const { agents, departmentId, open, tags, roomName, onhold } = this.requestParams();
let { createdAt, customFields, closedAt } = this.requestParams();
check(agents, Match.Maybe([String]));
check(roomName, Match.Maybe(String));
check(departmentId, Match.Maybe(String));
check(open, Match.Maybe(String));
+ check(onhold, Match.Maybe(String));
check(tags, Match.Maybe([String]));
const hasAdminAccess = hasPermission(this.userId, 'view-livechat-rooms');
@@ -51,6 +52,7 @@ API.v1.addRoute('livechat/rooms', { authRequired: true }, {
closedAt,
tags,
customFields,
+ onhold,
options: { offset, count, sort, fields },
})));
},
diff --git a/app/livechat/imports/server/rest/sms.js b/app/livechat/imports/server/rest/sms.js
index 4f29e7e997e0..ac180f4c652e 100644
--- a/app/livechat/imports/server/rest/sms.js
+++ b/app/livechat/imports/server/rest/sms.js
@@ -30,7 +30,7 @@ const defineDepartment = (idOrName) => {
return department && department._id;
};
-const defineVisitor = (smsNumber) => {
+const defineVisitor = (smsNumber, targetDepartment) => {
const visitor = LivechatVisitors.findOneVisitorByPhone(smsNumber);
let data = {
token: (visitor && visitor.token) || Random.id(),
@@ -45,9 +45,8 @@ const defineVisitor = (smsNumber) => {
});
}
- const department = defineDepartment(SMS.department);
- if (department) {
- data.department = department;
+ if (targetDepartment) {
+ data.department = targetDepartment;
}
const id = Livechat.registerGuest(data);
@@ -70,10 +69,15 @@ API.v1.addRoute('livechat/sms-incoming/:service', {
post() {
const SMSService = SMS.getService(this.urlParams.service);
const sms = SMSService.parse(this.bodyParams);
+ const { department } = this.queryParams;
+ let targetDepartment = defineDepartment(department || SMS.department);
+ if (!targetDepartment) {
+ targetDepartment = defineDepartment(SMS.department);
+ }
- const visitor = defineVisitor(sms.from);
+ const visitor = defineVisitor(sms.from, targetDepartment);
const { token } = visitor;
- const room = LivechatRooms.findOneOpenByVisitorToken(token);
+ const room = LivechatRooms.findOneOpenByVisitorTokenAndDepartmentId(token, targetDepartment);
const roomExists = !!room;
const location = normalizeLocationSharing(sms);
const rid = (room && room._id) || Random.id();
diff --git a/app/livechat/imports/server/rest/visitors.ts b/app/livechat/imports/server/rest/visitors.ts
new file mode 100644
index 000000000000..e75d4a955a2a
--- /dev/null
+++ b/app/livechat/imports/server/rest/visitors.ts
@@ -0,0 +1,47 @@
+
+import { check } from 'meteor/check';
+
+import { API } from '../../../../api/server';
+import { LivechatRooms } from '../../../../models/server';
+import { Messages } from '../../../../models/server/raw';
+import { normalizeMessagesForUser } from '../../../../utils/server/lib/normalizeMessagesForUser';
+import { canAccessRoom } from '../../../../authorization/server';
+import { IMessage } from '../../../../../definition/IMessage';
+
+API.v1.addRoute('livechat/:rid/messages', { authRequired: true, permissionsRequired: ['view-l-room'] }, {
+ async get() {
+ check(this.urlParams, {
+ rid: String,
+ });
+
+ const { offset, count } = this.getPaginationItems();
+ const { sort } = this.parseJsonQuery();
+
+ const room = LivechatRooms.findOneById(this.urlParams.rid);
+
+ if (!room) {
+ throw new Error('invalid-room');
+ }
+
+ if (!canAccessRoom(room, this.user)) {
+ throw new Error('not-allowed');
+ }
+
+ const cursor = Messages.findLivechatClosedMessages(this.urlParams.rid, {
+ sort: sort || { ts: -1 },
+ skip: offset,
+ limit: count,
+ });
+
+ const total = await cursor.count();
+
+ const messages = await cursor.toArray() as IMessage[];
+
+ return API.v1.success({
+ messages: normalizeMessagesForUser(messages, this.userId),
+ offset,
+ count,
+ total,
+ });
+ },
+});
diff --git a/app/livechat/lib/messageTypes.js b/app/livechat/lib/messageTypes.js
index bde52192cbe9..fb6fa4c10160 100644
--- a/app/livechat/lib/messageTypes.js
+++ b/app/livechat/lib/messageTypes.js
@@ -1,4 +1,6 @@
+import formatDistance from 'date-fns/formatDistance';
import { TAPi18n } from 'meteor/rocketchat:tap-i18n';
+import moment from 'moment';
import { MessageTypes } from '../../ui-utils';
@@ -81,6 +83,22 @@ MessageTypes.registerType({
message: 'New_videocall_request',
});
+MessageTypes.registerType({
+ id: 'livechat_webrtc_video_call',
+ render(message) {
+ if (message.msg === 'ended' && message.webRtcCallEndTs && message.ts) {
+ return TAPi18n.__('WebRTC_call_ended_message', {
+ callDuration: formatDistance(new Date(message.webRtcCallEndTs), new Date(message.ts)),
+ endTime: moment(message.webRtcCallEndTs).format('h:mm A'),
+ });
+ }
+ if (message.msg === 'declined' && message.webRtcCallEndTs) {
+ return TAPi18n.__('WebRTC_call_declined_message');
+ }
+ return message.msg;
+ },
+});
+
MessageTypes.registerType({
id: 'omnichannel_placed_chat_on_hold',
system: true,
diff --git a/app/livechat/server/api.js b/app/livechat/server/api.js
index 6a13dddc86bf..7aa0ee39c4a3 100644
--- a/app/livechat/server/api.js
+++ b/app/livechat/server/api.js
@@ -11,6 +11,7 @@ import '../imports/server/rest/triggers.js';
import '../imports/server/rest/integrations.js';
import '../imports/server/rest/messages.js';
import '../imports/server/rest/visitors.js';
+import '../imports/server/rest/visitors.ts';
import '../imports/server/rest/dashboards.js';
import '../imports/server/rest/queue.js';
import '../imports/server/rest/officeHour.js';
diff --git a/app/livechat/server/api/lib/departments.js b/app/livechat/server/api/lib/departments.js
index 0a70d1b6fca4..1e70a709444f 100644
--- a/app/livechat/server/api/lib/departments.js
+++ b/app/livechat/server/api/lib/departments.js
@@ -71,7 +71,7 @@ export async function findDepartmentsToAutocomplete({ uid, selector, onlyMyDepar
let { conditions = {} } = selector;
const options = {
- fields: {
+ projection: {
_id: 1,
name: 1,
},
diff --git a/app/livechat/server/api/lib/livechat.js b/app/livechat/server/api/lib/livechat.js
index a7a29598250f..b374e5139a99 100644
--- a/app/livechat/server/api/lib/livechat.js
+++ b/app/livechat/server/api/lib/livechat.js
@@ -1,7 +1,9 @@
import { Meteor } from 'meteor/meteor';
import { Random } from 'meteor/random';
+import { TAPi18n } from 'meteor/rocketchat:tap-i18n';
-import { LivechatRooms, LivechatVisitors, LivechatDepartment, LivechatTrigger, EmojiCustom } from '../../../../models/server';
+import { LivechatRooms, LivechatVisitors, LivechatDepartment, LivechatTrigger } from '../../../../models/server';
+import { EmojiCustom } from '../../../../models/server/raw';
import { Livechat } from '../../lib/Livechat';
import { callbacks } from '../../../../callbacks/server';
import { normalizeAgent } from '../../lib/Helper';
@@ -55,6 +57,7 @@ export function findOpenRoom(token, departmentId) {
departmentId: 1,
servedBy: 1,
open: 1,
+ callStatus: 1,
},
};
@@ -86,12 +89,12 @@ export function normalizeHttpHeaderData(headers = {}) {
const httpHeaders = Object.assign({}, headers);
return { httpHeaders };
}
-export function settings() {
+export async function settings() {
const initSettings = Livechat.getInitSettings();
const triggers = findTriggers();
const departments = findDepartments();
const sound = `${ Meteor.absoluteUrl() }sounds/chime.mp3`;
- const emojis = EmojiCustom.find().fetch();
+ const emojis = await EmojiCustom.find().toArray();
return {
enabled: initSettings.Livechat_enabled,
settings: {
@@ -100,7 +103,7 @@ export function settings() {
nameFieldRegistrationForm: initSettings.Livechat_name_field_registration_form,
emailFieldRegistrationForm: initSettings.Livechat_email_field_registration_form,
displayOfflineForm: initSettings.Livechat_display_offline_form,
- videoCall: initSettings.Livechat_videocall_enabled === true && initSettings.Jitsi_Enabled === true,
+ videoCall: initSettings.Omnichannel_call_provider === 'Jitsi' && initSettings.Jitsi_Enabled === true,
fileUpload: initSettings.Livechat_fileupload_enabled && initSettings.FileUpload_Enabled,
language: initSettings.Language,
transcript: initSettings.Livechat_enable_transcript,
@@ -116,10 +119,16 @@ export function settings() {
color: initSettings.Livechat_title_color,
offlineTitle: initSettings.Livechat_offline_title,
offlineColor: initSettings.Livechat_offline_title_color,
- actionLinks: [
- { icon: 'icon-videocam', i18nLabel: 'Accept', method_id: 'createLivechatCall', params: '' },
- { icon: 'icon-cancel', i18nLabel: 'Decline', method_id: 'denyLivechatCall', params: '' },
- ],
+ actionLinks: {
+ webrtc: [
+ { actionLinksAlignment: 'flex-start', i18nLabel: 'Join_call', label: TAPi18n.__('Join_call'), method_id: 'joinLivechatWebRTCCall' },
+ { i18nLabel: 'End_call', label: TAPi18n.__('End_call'), method_id: 'endLivechatWebRTCCall', danger: true },
+ ],
+ jitsi: [
+ { icon: 'icon-videocam', i18nLabel: 'Accept', method_id: 'createLivechatCall' },
+ { icon: 'icon-cancel', i18nLabel: 'Decline', method_id: 'denyLivechatCall' },
+ ],
+ },
},
messages: {
offlineMessage: initSettings.Livechat_offline_message,
diff --git a/app/livechat/server/api/lib/rooms.js b/app/livechat/server/api/lib/rooms.js
index 72d84803de15..957911737f0a 100644
--- a/app/livechat/server/api/lib/rooms.js
+++ b/app/livechat/server/api/lib/rooms.js
@@ -9,6 +9,7 @@ export async function findRooms({
closedAt,
tags,
customFields,
+ onhold,
options: {
offset,
count,
@@ -25,6 +26,7 @@ export async function findRooms({
closedAt,
tags,
customFields,
+ onhold: ['t', 'true', '1'].includes(onhold),
options: {
sort: sort || { ts: -1 },
offset,
diff --git a/app/livechat/server/api/lib/visitors.js b/app/livechat/server/api/lib/visitors.js
index c0366bf1ac69..d03566d6da99 100644
--- a/app/livechat/server/api/lib/visitors.js
+++ b/app/livechat/server/api/lib/visitors.js
@@ -72,6 +72,7 @@ export async function findChatHistory({ userId, roomId, visitorId, pagination: {
total,
};
}
+
export async function searchChats({ userId, roomId, visitorId, searchText, closedChatsOnly, servedChatsOnly: served, pagination: { offset, count, sort } }) {
if (!await hasPermissionAsync(userId, 'view-l-room')) {
throw new Error('error-not-authorized');
@@ -111,7 +112,7 @@ export async function findVisitorsToAutocomplete({ userId, selector }) {
const { exceptions = [], conditions = {} } = selector;
const options = {
- fields: {
+ projection: {
_id: 1,
name: 1,
username: 1,
diff --git a/app/livechat/server/api/v1/config.js b/app/livechat/server/api/v1/config.js
index e43509d4ba3c..f1a49c1ed395 100644
--- a/app/livechat/server/api/v1/config.js
+++ b/app/livechat/server/api/v1/config.js
@@ -17,7 +17,7 @@ API.v1.addRoute('livechat/config', {
return API.v1.success({ config: { enabled: false } });
}
- const config = settings();
+ const config = Promise.await(settings());
const { token, department } = this.queryParams;
const status = Livechat.online(department);
diff --git a/app/livechat/server/api/v1/message.js b/app/livechat/server/api/v1/message.js
index 178f571da490..0fd39bc5d086 100644
--- a/app/livechat/server/api/v1/message.js
+++ b/app/livechat/server/api/v1/message.js
@@ -108,7 +108,7 @@ API.v1.addRoute('livechat/message/:_id', {
}
if (message.file) {
- message = normalizeMessageFileUpload(message);
+ message = Promise.await(normalizeMessageFileUpload(message));
}
return API.v1.success({ message });
@@ -151,7 +151,7 @@ API.v1.addRoute('livechat/message/:_id', {
if (result) {
let message = Messages.findOneById(_id);
if (message.file) {
- message = normalizeMessageFileUpload(message);
+ message = Promise.await(normalizeMessageFileUpload(message));
}
return API.v1.success({ message });
@@ -191,7 +191,7 @@ API.v1.addRoute('livechat/message/:_id', {
throw new Meteor.Error('invalid-message');
}
- const result = Livechat.deleteMessage({ guest, message });
+ const result = Promise.await(Livechat.deleteMessage({ guest, message }));
if (result) {
return API.v1.success({
message: {
@@ -251,7 +251,7 @@ API.v1.addRoute('livechat/messages.history/:rid', {
const messages = loadMessageHistory({ userId: guest._id, rid, end, limit, ls, sort, offset, text })
.messages
- .map(normalizeMessageFileUpload);
+ .map((...args) => Promise.await(normalizeMessageFileUpload(...args)));
return API.v1.success({ messages });
} catch (e) {
return API.v1.failure(e);
diff --git a/app/livechat/server/api/v1/room.js b/app/livechat/server/api/v1/room.js
index 5dcbf8cf1dd0..88c9e4717110 100644
--- a/app/livechat/server/api/v1/room.js
+++ b/app/livechat/server/api/v1/room.js
@@ -166,7 +166,7 @@ API.v1.addRoute('livechat/room.survey', {
throw new Meteor.Error('invalid-room');
}
- const config = settings();
+ const config = Promise.await(settings());
if (!config.survey || !config.survey.items || !config.survey.values) {
throw new Meteor.Error('invalid-livechat-config');
}
diff --git a/app/livechat/server/api/v1/videoCall.js b/app/livechat/server/api/v1/videoCall.js
index 38b9c2d66491..6aef0c49537e 100644
--- a/app/livechat/server/api/v1/videoCall.js
+++ b/app/livechat/server/api/v1/videoCall.js
@@ -1,12 +1,15 @@
import { Meteor } from 'meteor/meteor';
import { Match, check } from 'meteor/check';
import { Random } from 'meteor/random';
+import { TAPi18n } from 'meteor/rocketchat:tap-i18n';
-import { Messages } from '../../../../models';
-import { settings as rcSettings } from '../../../../settings';
+import { Messages, Rooms } from '../../../../models';
+import { settings as rcSettings } from '../../../../settings/server';
import { API } from '../../../../api/server';
import { findGuest, getRoom, settings } from '../lib/livechat';
import { OmnichannelSourceType } from '../../../../../definition/IRoom';
+import { hasPermission, canSendMessage } from '../../../../authorization';
+import { Livechat } from '../../lib/Livechat';
API.v1.addRoute('livechat/video.call/:token', {
get() {
@@ -35,13 +38,13 @@ API.v1.addRoute('livechat/video.call/:token', {
},
};
const { room } = getRoom({ guest, rid, roomInfo });
- const config = settings();
- if (!config.theme || !config.theme.actionLinks) {
+ const config = Promise.await(settings());
+ if (!config.theme || !config.theme.actionLinks || !config.theme.actionLinks.jitsi) {
throw new Meteor.Error('invalid-livechat-config');
}
Messages.createWithTypeRoomIdMessageAndUser('livechat_video_call', room._id, '', guest, {
- actionLinks: config.theme.actionLinks,
+ actionLinks: config.theme.actionLinks.jitsi,
});
let rname;
if (rcSettings.get('Jitsi_URL_Room_Hash')) {
@@ -63,3 +66,102 @@ API.v1.addRoute('livechat/video.call/:token', {
}
},
});
+
+API.v1.addRoute('livechat/webrtc.call', { authRequired: true }, {
+ get() {
+ try {
+ check(this.queryParams, {
+ rid: Match.Maybe(String),
+ });
+
+ if (!hasPermission(this.userId, 'view-l-room')) {
+ return API.v1.unauthorized();
+ }
+
+ const room = canSendMessage(this.queryParams.rid, {
+ uid: this.userId,
+ username: this.user.username,
+ type: this.user.type,
+ });
+ if (!room) {
+ throw new Meteor.Error('invalid-room');
+ }
+
+ const webrtcCallingAllowed = (rcSettings.get('WebRTC_Enabled') === true) && (rcSettings.get('Omnichannel_call_provider') === 'WebRTC');
+ if (!webrtcCallingAllowed) {
+ throw new Meteor.Error('webRTC calling not enabled');
+ }
+
+ const config = Promise.await(settings());
+ if (!config.theme || !config.theme.actionLinks || !config.theme.actionLinks.webrtc) {
+ throw new Meteor.Error('invalid-livechat-config');
+ }
+
+ let { callStatus } = room;
+
+ if (!callStatus || callStatus === 'ended' || callStatus === 'declined') {
+ callStatus = 'ringing';
+ Promise.await(Rooms.setCallStatusAndCallStartTime(room._id, callStatus));
+ Promise.await(Messages.createWithTypeRoomIdMessageAndUser(
+ 'livechat_webrtc_video_call',
+ room._id,
+ TAPi18n.__('Join_my_room_to_start_the_video_call'),
+ this.user,
+ {
+ actionLinks: config.theme.actionLinks.webrtc,
+ },
+ ));
+ }
+ const videoCall = {
+ rid: room._id,
+ provider: 'webrtc',
+ callStatus,
+ };
+ return API.v1.success({ videoCall });
+ } catch (e) {
+ return API.v1.failure(e);
+ }
+ },
+});
+
+API.v1.addRoute('livechat/webrtc.call/:callId', { authRequired: true }, {
+ put() {
+ try {
+ check(this.urlParams, {
+ callId: String,
+ });
+
+ check(this.bodyParams, {
+ rid: Match.Maybe(String),
+ status: Match.Maybe(String),
+ });
+
+ const { callId } = this.urlParams;
+ const { rid, status } = this.bodyParams;
+
+ if (!hasPermission(this.userId, 'view-l-room')) {
+ return API.v1.unauthorized();
+ }
+
+ const room = canSendMessage(rid, {
+ uid: this.userId,
+ username: this.user.username,
+ type: this.user.type,
+ });
+ if (!room) {
+ throw new Meteor.Error('invalid-room');
+ }
+
+ const call = Promise.await(Messages.findOneById(callId));
+ if (!call || call.t !== 'livechat_webrtc_video_call') {
+ throw new Meteor.Error('invalid-callId');
+ }
+
+ Livechat.updateCallStatus(callId, rid, status, this.user);
+
+ return API.v1.success({ status });
+ } catch (e) {
+ return API.v1.failure(e);
+ }
+ },
+});
diff --git a/app/livechat/server/api/v1/visitor.js b/app/livechat/server/api/v1/visitor.js
index 98007540876c..dc4e012839ba 100644
--- a/app/livechat/server/api/v1/visitor.js
+++ b/app/livechat/server/api/v1/visitor.js
@@ -128,6 +128,30 @@ API.v1.addRoute('livechat/visitor/:token/room', { authRequired: true }, {
},
});
+API.v1.addRoute('livechat/visitor.callStatus', {
+ post() {
+ try {
+ check(this.bodyParams, {
+ token: String,
+ callStatus: String,
+ rid: String,
+ callId: String,
+ });
+
+ const { token, callStatus, rid, callId } = this.bodyParams;
+ const guest = findGuest(token);
+ if (!guest) {
+ throw new Meteor.Error('invalid-token');
+ }
+ const status = callStatus;
+ Livechat.updateCallStatus(callId, rid, status, guest);
+ return API.v1.success({ token, callStatus });
+ } catch (e) {
+ return API.v1.failure(e);
+ }
+ },
+});
+
API.v1.addRoute('livechat/visitor.status', {
post() {
try {
diff --git a/app/livechat/server/business-hour/BusinessHourManager.ts b/app/livechat/server/business-hour/BusinessHourManager.ts
index e1b5558d0acd..04505776f32b 100644
--- a/app/livechat/server/business-hour/BusinessHourManager.ts
+++ b/app/livechat/server/business-hour/BusinessHourManager.ts
@@ -1,11 +1,11 @@
import moment from 'moment';
import { ILivechatBusinessHour, LivechatBusinessHourTypes } from '../../../../definition/ILivechatBusinessHour';
-import { ICronJobs } from '../../../utils/server/lib/cron/Cronjobs';
import { IBusinessHourBehavior, IBusinessHourType } from './AbstractBusinessHour';
import { settings } from '../../../settings/server';
import { callbacks } from '../../../callbacks/server';
import { Users } from '../../../models/server/raw';
+import { ICronJobs } from '../../../../definition/ICronJobs';
const cronJobDayDict: Record = {
Sunday: 0,
diff --git a/app/livechat/server/config.ts b/app/livechat/server/config.ts
index f0a19e3246e6..0d37ad8bf9dd 100644
--- a/app/livechat/server/config.ts
+++ b/app/livechat/server/config.ts
@@ -375,16 +375,6 @@ Meteor.startup(function() {
enableQuery: omnichannelEnabledQuery,
});
- this.add('Livechat_videocall_enabled', false, {
- type: 'boolean',
- group: 'Omnichannel',
- section: 'Livechat',
- public: true,
- i18nLabel: 'Videocall_enabled',
- i18nDescription: 'Beta_feature_Depends_on_Video_Conference_to_be_enabled',
- enableQuery: [{ _id: 'Jitsi_Enabled', value: true }, omnichannelEnabledQuery],
- });
-
this.add('Livechat_fileupload_enabled', true, {
type: 'boolean',
group: 'Omnichannel',
@@ -616,5 +606,21 @@ Meteor.startup(function() {
i18nDescription: 'Time_in_seconds',
enableQuery: omnichannelEnabledQuery,
});
+
+ this.add('Omnichannel_call_provider', 'none', {
+ type: 'select',
+ public: true,
+ group: 'Omnichannel',
+ section: 'Video_and_Audio_Call',
+ values: [
+ { key: 'none', i18nLabel: 'None' },
+ { key: 'Jitsi', i18nLabel: 'Jitsi' },
+ { key: 'WebRTC', i18nLabel: 'WebRTC' },
+ ],
+ i18nDescription: 'Feature_depends_on_selected_call_provider_to_be_enabled_from_administration_settings',
+ i18nLabel: 'Call_provider',
+ alert: 'The WebRTC provider is currently in alpha!
We recommend using Firefox Browser for this feature since there are some known bugs within other browsers that still need to be fixed.
Please report bugs to github.com/RocketChat/Rocket.Chat/issues',
+ enableQuery: omnichannelEnabledQuery,
+ });
});
});
diff --git a/app/livechat/server/hooks/saveAnalyticsData.js b/app/livechat/server/hooks/saveAnalyticsData.js
index 4ca8832c153d..96d336c228ca 100644
--- a/app/livechat/server/hooks/saveAnalyticsData.js
+++ b/app/livechat/server/hooks/saveAnalyticsData.js
@@ -14,7 +14,7 @@ callbacks.add('afterSaveMessage', function(message, room) {
}
if (message.file) {
- message = normalizeMessageFileUpload(message);
+ message = Promise.await(normalizeMessageFileUpload(message));
}
const now = new Date();
diff --git a/app/livechat/server/hooks/sendToCRM.js b/app/livechat/server/hooks/sendToCRM.js
index ac1ec663904b..94e4cfcd22eb 100644
--- a/app/livechat/server/hooks/sendToCRM.js
+++ b/app/livechat/server/hooks/sendToCRM.js
@@ -84,7 +84,7 @@ function sendToCRM(type, room, includeMessages = true) {
}
const { u } = message;
- postData.messages.push(normalizeMessageFileUpload({ u, ...msg }));
+ postData.messages.push(Promise.await(normalizeMessageFileUpload({ u, ...msg })));
});
}
diff --git a/app/livechat/server/hooks/sendToFacebook.js b/app/livechat/server/hooks/sendToFacebook.js
index 1af4767e3656..7c1b00f31211 100644
--- a/app/livechat/server/hooks/sendToFacebook.js
+++ b/app/livechat/server/hooks/sendToFacebook.js
@@ -29,7 +29,7 @@ callbacks.add('afterSaveMessage', function(message, room) {
}
if (message.file) {
- message = normalizeMessageFileUpload(message);
+ message = Promise.await(normalizeMessageFileUpload(message));
}
OmniChannel.reply({
diff --git a/app/livechat/server/lib/Analytics.js b/app/livechat/server/lib/Analytics.js
index c4251fbd1bce..b4aa71af346f 100644
--- a/app/livechat/server/lib/Analytics.js
+++ b/app/livechat/server/lib/Analytics.js
@@ -2,6 +2,7 @@ import { TAPi18n } from 'meteor/rocketchat:tap-i18n';
import moment from 'moment';
import { LivechatRooms } from '../../../models';
+import { LivechatRooms as LivechatRoomsRaw } from '../../../models/server/raw';
import { secondsToHHMMSS } from '../../../utils/server';
import { getTimezone } from '../../../utils/server/lib/getTimezone';
import { Logger } from '../../../logger';
@@ -288,8 +289,8 @@ export const Analytics = {
const totalMessagesInHour = new Map(); // total messages in hour 0, 1, ... 23 of weekday
const days = to.diff(from, 'days') + 1; // total days
- const summarize = (m) => ({ metrics, msgs }) => {
- if (metrics && !metrics.chatDuration) {
+ const summarize = (m) => ({ metrics, msgs, onHold = false }) => {
+ if (metrics && !metrics.chatDuration && !onHold) {
openConversations++;
}
totalMessages += msgs;
@@ -337,13 +338,17 @@ export const Analytics = {
to: utcBusiestHour >= 0 ? moment.utc().set({ hour: utcBusiestHour }).tz(timezone).format('hA') : '-',
from: utcBusiestHour >= 0 ? moment.utc().set({ hour: utcBusiestHour }).subtract(1, 'hour').tz(timezone).format('hA') : '',
};
+ const onHoldConversations = Promise.await(LivechatRoomsRaw.getOnHoldConversationsBetweenDate(from, to, departmentId));
- const data = [{
+ return [{
title: 'Total_conversations',
value: totalConversations,
}, {
title: 'Open_conversations',
value: openConversations,
+ }, {
+ title: 'On_Hold_conversations',
+ value: onHoldConversations,
}, {
title: 'Total_messages',
value: totalMessages,
@@ -357,8 +362,6 @@ export const Analytics = {
title: 'Busiest_time',
value: `${ busiestHour.from }${ busiestHour.to ? `- ${ busiestHour.to }` : '' }`,
}];
-
- return data;
},
/**
diff --git a/app/livechat/server/lib/Livechat.js b/app/livechat/server/lib/Livechat.js
index aa2f93eb698d..78d54cd5f1e6 100644
--- a/app/livechat/server/lib/Livechat.js
+++ b/app/livechat/server/lib/Livechat.js
@@ -222,7 +222,7 @@ export const Livechat = {
return true;
},
- deleteMessage({ guest, message }) {
+ async deleteMessage({ guest, message }) {
Livechat.logger.debug(`Attempting to delete a message by visitor ${ guest._id }`);
check(message, Match.ObjectIncluding({ _id: String }));
@@ -239,7 +239,7 @@ export const Livechat = {
throw new Meteor.Error('error-action-not-allowed', 'Message deleting not allowed', { method: 'livechatDeleteMessage' });
}
- deleteMessage(message, guest);
+ await deleteMessage(message, guest);
return true;
},
@@ -514,7 +514,7 @@ export const Livechat = {
'Livechat_offline_success_message',
'Livechat_offline_form_unavailable',
'Livechat_display_offline_form',
- 'Livechat_videocall_enabled',
+ 'Omnichannel_call_provider',
'Jitsi_Enabled',
'Language',
'Livechat_enable_transcript',
@@ -598,7 +598,7 @@ export const Livechat = {
const user = Users.findOneById(userId);
const { _id, username, name } = user;
const transferredBy = normalizeTransferredByData({ _id, username, name }, room);
- this.transfer(room, guest, { roomId: room._id, transferredBy, departmentId: guest.department });
+ Promise.await(this.transfer(room, guest, { roomId: room._id, transferredBy, departmentId: guest.department }));
});
},
@@ -1278,6 +1278,12 @@ export const Livechat = {
};
LivechatVisitors.updateById(contactId, updateUser);
},
+ updateCallStatus(callId, rid, status, user) {
+ Rooms.setCallStatus(rid, status);
+ if (status === 'ended' || status === 'declined') {
+ return updateMessage({ _id: callId, msg: status, actionLinks: [], webRtcCallEndTs: new Date() }, user);
+ }
+ },
};
settings.watch('Livechat_history_monitor_type', (value) => {
diff --git a/app/livechat/server/lib/analytics/dashboards.js b/app/livechat/server/lib/analytics/dashboards.js
index 18bdca630033..70dc1c7925ff 100644
--- a/app/livechat/server/lib/analytics/dashboards.js
+++ b/app/livechat/server/lib/analytics/dashboards.js
@@ -25,6 +25,7 @@ const findAllChatsStatusAsync = async ({
open: await LivechatRooms.countAllOpenChatsBetweenDate({ start, end, departmentId }),
closed: await LivechatRooms.countAllClosedChatsBetweenDate({ start, end, departmentId }),
queued: await LivechatRooms.countAllQueuedChatsBetweenDate({ start, end, departmentId }),
+ onhold: await LivechatRooms.getOnHoldConversationsBetweenDate(start, end, departmentId),
};
};
@@ -193,7 +194,7 @@ const getConversationsMetricsAsync = async ({
utcOffset: user.utcOffset,
language: user.language || settings.get('Language') || 'en',
});
- const metrics = ['Total_conversations', 'Open_conversations', 'Total_messages'];
+ const metrics = ['Total_conversations', 'Open_conversations', 'On_Hold_conversations', 'Total_messages'];
const visitorsCount = await LivechatVisitors.getVisitorsBetweenDate({ start, end, department: departmentId }).count();
return {
totalizers: [
@@ -213,13 +214,20 @@ const findAllChatMetricsByAgentAsync = async ({
}
const open = await LivechatRooms.countAllOpenChatsByAgentBetweenDate({ start, end, departmentId });
const closed = await LivechatRooms.countAllClosedChatsByAgentBetweenDate({ start, end, departmentId });
+ const onhold = await LivechatRooms.countAllOnHoldChatsByAgentBetweenDate({ start, end, departmentId });
const result = {};
(open || []).forEach((agent) => {
- result[agent._id] = { open: agent.chats, closed: 0 };
+ result[agent._id] = { open: agent.chats, closed: 0, onhold: 0 };
});
(closed || []).forEach((agent) => {
result[agent._id] = { open: result[agent._id] ? result[agent._id].open : 0, closed: agent.chats };
});
+ (onhold || []).forEach((agent) => {
+ result[agent._id] = {
+ ...result[agent._id],
+ onhold: agent.chats,
+ };
+ });
return result;
};
diff --git a/app/livechat/server/lib/stream/agentStatus.ts b/app/livechat/server/lib/stream/agentStatus.ts
index ed46bacbd0f8..12985a42f58d 100644
--- a/app/livechat/server/lib/stream/agentStatus.ts
+++ b/app/livechat/server/lib/stream/agentStatus.ts
@@ -2,6 +2,9 @@ import { Meteor } from 'meteor/meteor';
import { Livechat } from '../Livechat';
import { settings } from '../../../../settings/server';
+import { Logger } from '../../../../logger/server';
+
+const logger = new Logger('AgentStatusWatcher');
export let monitorAgents = false;
let actionTimeout = 60000;
@@ -64,12 +67,19 @@ export const onlineAgents = {
onlineAgents.users.delete(userId);
onlineAgents.queue.delete(userId);
- if (action === 'close') {
- return Livechat.closeOpenChats(userId, comment);
- }
+ try {
+ if (action === 'close') {
+ return Livechat.closeOpenChats(userId, comment);
+ }
- if (action === 'forward') {
- return Livechat.forwardOpenChats(userId);
+ if (action === 'forward') {
+ return Livechat.forwardOpenChats(userId);
+ }
+ } catch (e) {
+ logger.error({
+ msg: `Cannot perform action ${ action }`,
+ err: e,
+ });
}
}),
};
diff --git a/app/livechat/server/methods/getInitialData.js b/app/livechat/server/methods/getInitialData.js
index 9b7a2f22a705..bac76ce8d49a 100644
--- a/app/livechat/server/methods/getInitialData.js
+++ b/app/livechat/server/methods/getInitialData.js
@@ -75,7 +75,7 @@ Meteor.methods({
info.offlineUnavailableMessage = initSettings.Livechat_offline_form_unavailable;
info.displayOfflineForm = initSettings.Livechat_display_offline_form;
info.language = initSettings.Language;
- info.videoCall = initSettings.Livechat_videocall_enabled === true && initSettings.Jitsi_Enabled === true;
+ info.videoCall = initSettings.Omnichannel_call_provider === 'Jitsi' && initSettings.Jitsi_Enabled === true;
info.fileUpload = initSettings.Livechat_fileupload_enabled && initSettings.FileUpload_Enabled;
info.transcript = initSettings.Livechat_enable_transcript;
info.transcriptMessage = initSettings.Livechat_transcript_message;
diff --git a/app/livechat/server/methods/saveIntegration.js b/app/livechat/server/methods/saveIntegration.js
index 38288eab9e0d..d9585643783e 100644
--- a/app/livechat/server/methods/saveIntegration.js
+++ b/app/livechat/server/methods/saveIntegration.js
@@ -3,7 +3,6 @@ import s from 'underscore.string';
import { hasPermission } from '../../../authorization';
import { Settings } from '../../../models/server';
-import { settings } from '../../../settings';
Meteor.methods({
'livechat:saveIntegration'(values) {
@@ -16,39 +15,39 @@ Meteor.methods({
}
if (typeof values.Livechat_secret_token !== 'undefined') {
- settings.updateValueById('Livechat_secret_token', s.trim(values.Livechat_secret_token));
+ Settings.updateValueById('Livechat_secret_token', s.trim(values.Livechat_secret_token));
}
if (typeof values.Livechat_webhook_on_start !== 'undefined') {
- settings.updateValueById('Livechat_webhook_on_start', !!values.Livechat_webhook_on_start);
+ Settings.updateValueById('Livechat_webhook_on_start', !!values.Livechat_webhook_on_start);
}
if (typeof values.Livechat_webhook_on_close !== 'undefined') {
- settings.updateValueById('Livechat_webhook_on_close', !!values.Livechat_webhook_on_close);
+ Settings.updateValueById('Livechat_webhook_on_close', !!values.Livechat_webhook_on_close);
}
if (typeof values.Livechat_webhook_on_chat_taken !== 'undefined') {
- settings.updateValueById('Livechat_webhook_on_chat_taken', !!values.Livechat_webhook_on_chat_taken);
+ Settings.updateValueById('Livechat_webhook_on_chat_taken', !!values.Livechat_webhook_on_chat_taken);
}
if (typeof values.Livechat_webhook_on_chat_queued !== 'undefined') {
- settings.updateValueById('Livechat_webhook_on_chat_queued', !!values.Livechat_webhook_on_chat_queued);
+ Settings.updateValueById('Livechat_webhook_on_chat_queued', !!values.Livechat_webhook_on_chat_queued);
}
if (typeof values.Livechat_webhook_on_forward !== 'undefined') {
- settings.updateValueById('Livechat_webhook_on_forward', !!values.Livechat_webhook_on_forward);
+ Settings.updateValueById('Livechat_webhook_on_forward', !!values.Livechat_webhook_on_forward);
}
if (typeof values.Livechat_webhook_on_offline_msg !== 'undefined') {
- settings.updateValueById('Livechat_webhook_on_offline_msg', !!values.Livechat_webhook_on_offline_msg);
+ Settings.updateValueById('Livechat_webhook_on_offline_msg', !!values.Livechat_webhook_on_offline_msg);
}
if (typeof values.Livechat_webhook_on_visitor_message !== 'undefined') {
- settings.updateValueById('Livechat_webhook_on_visitor_message', !!values.Livechat_webhook_on_visitor_message);
+ Settings.updateValueById('Livechat_webhook_on_visitor_message', !!values.Livechat_webhook_on_visitor_message);
}
if (typeof values.Livechat_webhook_on_agent_message !== 'undefined') {
- settings.updateValueById('Livechat_webhook_on_agent_message', !!values.Livechat_webhook_on_agent_message);
+ Settings.updateValueById('Livechat_webhook_on_agent_message', !!values.Livechat_webhook_on_agent_message);
}
},
});
diff --git a/app/livechat/server/sendMessageBySMS.js b/app/livechat/server/sendMessageBySMS.js
index 6094d1dc62a3..5e3bd1f13342 100644
--- a/app/livechat/server/sendMessageBySMS.js
+++ b/app/livechat/server/sendMessageBySMS.js
@@ -31,7 +31,7 @@ callbacks.add('afterSaveMessage', function(message, room) {
let extraData;
if (message.file) {
- message = normalizeMessageFileUpload(message);
+ message = Promise.await(normalizeMessageFileUpload(message));
const { fileUpload, rid, u: { _id: userId } = {} } = message;
extraData = Object.assign({}, { rid, userId, fileUpload });
}
diff --git a/app/livechat/server/statistics/LivechatAgentActivityMonitor.js b/app/livechat/server/statistics/LivechatAgentActivityMonitor.js
index 1066e112f027..2c894afe4f1f 100644
--- a/app/livechat/server/statistics/LivechatAgentActivityMonitor.js
+++ b/app/livechat/server/statistics/LivechatAgentActivityMonitor.js
@@ -3,7 +3,8 @@ import { Meteor } from 'meteor/meteor';
import { SyncedCron } from 'meteor/littledata:synced-cron';
import { callbacks } from '../../../callbacks/server';
-import { LivechatAgentActivity, Sessions, Users } from '../../../models/server';
+import { LivechatAgentActivity, Users } from '../../../models/server';
+import { Sessions } from '../../../models/server/raw';
const formatDate = (dateTime = new Date()) => ({
date: parseInt(moment(dateTime).format('YYYYMMDD')),
@@ -12,7 +13,6 @@ const formatDate = (dateTime = new Date()) => ({
export class LivechatAgentActivityMonitor {
constructor() {
this._started = false;
- this._handleMeteorConnection = this._handleMeteorConnection.bind(this);
this._handleAgentStatusChanged = this._handleAgentStatusChanged.bind(this);
this._handleUserStatusLivechatChanged = this._handleUserStatusLivechatChanged.bind(this);
this._name = 'Livechat Agent Activity Monitor';
@@ -41,7 +41,7 @@ export class LivechatAgentActivityMonitor {
return;
}
this._startMonitoring();
- Meteor.onConnection(this._handleMeteorConnection);
+ Meteor.onConnection((connection) => this._handleMeteorConnection(connection));
callbacks.add('livechat.agentStatusChanged', this._handleAgentStatusChanged);
callbacks.add('livechat.setUserStatusLivechat', this._handleUserStatusLivechatChanged);
this._started = true;
@@ -75,12 +75,12 @@ export class LivechatAgentActivityMonitor {
}
}
- _handleMeteorConnection(connection) {
+ async _handleMeteorConnection(connection) {
if (!this.isRunning()) {
return;
}
- const session = Sessions.findOne({ sessionId: connection.id });
+ const session = await Sessions.findOne({ sessionId: connection.id });
if (!session) {
return;
}
diff --git a/app/mailer/server/api.ts b/app/mailer/server/api.ts
index fe6c593e18d3..c59cd2151fd3 100644
--- a/app/mailer/server/api.ts
+++ b/app/mailer/server/api.ts
@@ -132,7 +132,7 @@ settings.watchMultiple(['Email_Header', 'Email_Footer'], () => {
export const rfcMailPatternWithName = /^(?:.*<)?([a-zA-Z0-9.!#$%&'*+\/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*)(?:>?)$/;
-export const checkAddressFormat = (from: string): boolean => rfcMailPatternWithName.test(from);
+export const checkAddressFormat = (adresses: string | string[]): boolean => ([] as string[]).concat(adresses).every((address) => rfcMailPatternWithName.test(address));
export const sendNoWrap = ({
to,
@@ -152,7 +152,7 @@ export const sendNoWrap = ({
headers?: string;
}): void => {
if (!checkAddressFormat(to)) {
- return;
+ throw new Meteor.Error('invalid email');
}
if (!text) {
diff --git a/app/mailer/tests/api.spec.ts b/app/mailer/tests/api.spec.ts
index 4c858c1b23e5..5bb1397c62e7 100644
--- a/app/mailer/tests/api.spec.ts
+++ b/app/mailer/tests/api.spec.ts
@@ -1,5 +1,4 @@
-/* eslint-env mocha */
-import assert from 'assert';
+import { expect } from 'chai';
import { replaceVariables } from '../server/replaceVariables';
@@ -13,55 +12,55 @@ describe('Mailer-API', function() {
describe('single key', function functionName() {
it(`should be equal to test ${ i18n.key }`, () => {
- assert.strictEqual(`test ${ i18n.key }`, replaceVariables('test {key}', (_match, key) => i18n[key]));
+ expect(`test ${ i18n.key }`).to.be.equal(replaceVariables('test {key}', (_match, key) => i18n[key]));
});
});
describe('multiple keys', function functionName() {
it(`should be equal to test ${ i18n.key } and ${ i18n.key }`, () => {
- assert.strictEqual(`test ${ i18n.key } and ${ i18n.key }`, replaceVariables('test {key} and {key}', (_match, key) => i18n[key]));
+ expect(`test ${ i18n.key } and ${ i18n.key }`).to.be.equal(replaceVariables('test {key} and {key}', (_match, key) => i18n[key]));
});
});
describe('key with a trailing space', function functionName() {
it(`should be equal to test ${ i18n.key }`, () => {
- assert.strictEqual(`test ${ i18n.key }`, replaceVariables('test {key }', (_match, key) => i18n[key]));
+ expect(`test ${ i18n.key }`).to.be.equal(replaceVariables('test {key }', (_match, key) => i18n[key]));
});
});
describe('key with a leading space', function functionName() {
it(`should be equal to test ${ i18n.key }`, () => {
- assert.strictEqual(`test ${ i18n.key }`, replaceVariables('test { key}', (_match, key) => i18n[key]));
+ expect(`test ${ i18n.key }`).to.be.equal(replaceVariables('test { key}', (_match, key) => i18n[key]));
});
});
describe('key with leading and trailing spaces', function functionName() {
it(`should be equal to test ${ i18n.key }`, () => {
- assert.strictEqual(`test ${ i18n.key }`, replaceVariables('test { key }', (_match, key) => i18n[key]));
+ expect(`test ${ i18n.key }`).to.be.equal(replaceVariables('test { key }', (_match, key) => i18n[key]));
});
});
describe('key with multiple words', function functionName() {
it(`should be equal to test ${ i18n.key }`, () => {
- assert.strictEqual(`test ${ i18n.key }`, replaceVariables('test {key ignore}', (_match, key) => i18n[key]));
+ expect(`test ${ i18n.key }`).to.be.equal(replaceVariables('test {key ignore}', (_match, key) => i18n[key]));
});
});
describe('key with multiple opening brackets', function functionName() {
it(`should be equal to test {${ i18n.key }`, () => {
- assert.strictEqual(`test {${ i18n.key }`, replaceVariables('test {{key}', (_match, key) => i18n[key]));
+ expect(`test {${ i18n.key }`).to.be.equal(replaceVariables('test {{key}', (_match, key) => i18n[key]));
});
});
describe('key with multiple closing brackets', function functionName() {
it(`should be equal to test ${ i18n.key }}`, () => {
- assert.strictEqual(`test ${ i18n.key }}`, replaceVariables('test {key}}', (_match, key) => i18n[key]));
+ expect(`test ${ i18n.key }}`).to.be.equal(replaceVariables('test {key}}', (_match, key) => i18n[key]));
});
});
describe('key with multiple opening and closing brackets', function functionName() {
it(`should be equal to test {${ i18n.key }}`, () => {
- assert.strictEqual(`test {${ i18n.key }}`, replaceVariables('test {{key}}', (_match, key) => i18n[key]));
+ expect(`test {${ i18n.key }}`).to.be.equal(replaceVariables('test {{key}}', (_match, key) => i18n[key]));
});
});
});
diff --git a/app/markdown/tests/client.tests.js b/app/markdown/tests/client.tests.js
index 83567dff6323..8d741f696ddb 100644
--- a/app/markdown/tests/client.tests.js
+++ b/app/markdown/tests/client.tests.js
@@ -1,8 +1,6 @@
-/* eslint-env mocha */
-import 'babel-polyfill';
-import assert from 'assert';
-
import './client.mocks.js';
+
+import { expect } from 'chai';
import { escapeHTML } from '@rocket.chat/string-helpers';
import { original } from '../lib/parser/original/original';
@@ -375,7 +373,7 @@ const blockcodeFiltered = {
'Here```code```lies': 'Herecodelies',
};
-const defaultObjectTest = (result, object, objectKey) => assert.equal(result.html, object[objectKey]);
+const defaultObjectTest = (result, object, objectKey) => expect(result.html).to.be.equal(object[objectKey]);
const testObject = (object, parser = original, test = defaultObjectTest) => {
Object.keys(object).forEach((objectKey) => {
@@ -435,7 +433,7 @@ describe('Filtered', function() {
describe('blockcodeFilter', () => testObject(blockcodeFiltered, filtered));
});
-// describe.only('Marked', function() {
+// describe('Marked', function() {
// describe('Bold', () => testObject(bold, marked));
// describe('Italic', () => testObject(italic, marked));
diff --git a/app/mentions/tests/client.tests.js b/app/mentions/tests/client.tests.js
index 5854ec14ba6a..e90249dcf23c 100644
--- a/app/mentions/tests/client.tests.js
+++ b/app/mentions/tests/client.tests.js
@@ -1,11 +1,9 @@
-/* eslint-env mocha */
-import 'babel-polyfill';
-import assert from 'assert';
+import { expect } from 'chai';
import { MentionsParser } from '../lib/MentionsParser';
let mentionsParser;
-beforeEach(function functionName() {
+beforeEach(() => {
mentionsParser = new MentionsParser({
pattern: '[0-9a-zA-Z-_.]+',
me: () => 'me',
@@ -17,15 +15,15 @@ describe('Mention', function() {
const regexp = '[0-9a-zA-Z-_.]+';
beforeEach(() => { mentionsParser.pattern = () => regexp; });
- describe('by function', function functionName() {
+ describe('by function', () => {
it(`should be equal to ${ regexp }`, () => {
- assert.equal(regexp, mentionsParser.pattern);
+ expect(regexp).to.be.equal(mentionsParser.pattern);
});
});
- describe('by const', function functionName() {
+ describe('by const', () => {
it(`should be equal to ${ regexp }`, () => {
- assert.equal(regexp, mentionsParser.pattern);
+ expect(regexp).to.be.equal(mentionsParser.pattern);
});
});
});
@@ -33,15 +31,15 @@ describe('Mention', function() {
describe('get useRealName', () => {
beforeEach(() => { mentionsParser.useRealName = () => true; });
- describe('by function', function functionName() {
+ describe('by function', () => {
it('should be true', () => {
- assert.equal(true, mentionsParser.useRealName);
+ expect(true).to.be.equal(mentionsParser.useRealName);
});
});
- describe('by const', function functionName() {
+ describe('by const', () => {
it('should be true', () => {
- assert.equal(true, mentionsParser.useRealName);
+ expect(true).to.be.equal(mentionsParser.useRealName);
});
});
});
@@ -49,24 +47,24 @@ describe('Mention', function() {
describe('get me', () => {
const me = 'me';
- describe('by function', function functionName() {
+ describe('by function', () => {
beforeEach(() => { mentionsParser.me = () => me; });
it(`should be equal to ${ me }`, () => {
- assert.equal(me, mentionsParser.me);
+ expect(me).to.be.equal(mentionsParser.me);
});
});
- describe('by const', function functionName() {
+ describe('by const', () => {
beforeEach(() => { mentionsParser.me = me; });
it(`should be equal to ${ me }`, () => {
- assert.equal(me, mentionsParser.me);
+ expect(me).to.be.equal(mentionsParser.me);
});
});
});
- describe('getUserMentions', function functionName() {
+ describe('getUserMentions', () => {
describe('for simple text, no mentions', () => {
const result = [];
[
@@ -75,7 +73,7 @@ describe('Mention', function() {
]
.forEach((text) => {
it(`should return "${ JSON.stringify(result) }" from "${ text }"`, () => {
- assert.deepEqual(result, mentionsParser.getUserMentions(text));
+ expect(result).to.be.deep.equal(mentionsParser.getUserMentions(text));
});
});
});
@@ -93,20 +91,20 @@ describe('Mention', function() {
]
.forEach((text) => {
it(`should return "${ JSON.stringify(result) }" from "${ text }"`, () => {
- assert.deepEqual(result, mentionsParser.getUserMentions(text));
+ expect(result).to.be.deep.equal(mentionsParser.getUserMentions(text));
});
});
it.skip('should return without the "." from "@rocket.cat."', () => {
- assert.deepEqual(result, mentionsParser.getUserMentions('@rocket.cat.'));
+ expect(result).to.be.deep.equal(mentionsParser.getUserMentions('@rocket.cat.'));
});
it.skip('should return without the "_" from "@rocket.cat_"', () => {
- assert.deepEqual(result, mentionsParser.getUserMentions('@rocket.cat_'));
+ expect(result).to.be.deep.equal(mentionsParser.getUserMentions('@rocket.cat_'));
});
it.skip('should return without the "-" from "@rocket.cat-"', () => {
- assert.deepEqual(result, mentionsParser.getUserMentions('@rocket.cat-'));
+ expect(result).to.be.deep.equal(mentionsParser.getUserMentions('@rocket.cat-'));
});
});
@@ -121,13 +119,13 @@ describe('Mention', function() {
]
.forEach((text) => {
it(`should return "${ JSON.stringify(result) }" from "${ text }"`, () => {
- assert.deepEqual(result, mentionsParser.getUserMentions(text));
+ expect(result).to.be.deep.equal(mentionsParser.getUserMentions(text));
});
});
});
});
- describe('getChannelMentions', function functionName() {
+ describe('getChannelMentions', () => {
describe('for simple text, no mentions', () => {
const result = [];
[
@@ -136,7 +134,7 @@ describe('Mention', function() {
]
.forEach((text) => {
it(`should return "${ JSON.stringify(result) }" from "${ text }"`, () => {
- assert.deepEqual(result, mentionsParser.getChannelMentions(text));
+ expect(result).to.be.deep.equal(mentionsParser.getChannelMentions(text));
});
});
});
@@ -151,20 +149,20 @@ describe('Mention', function() {
'hello #general, how are you?',
].forEach((text) => {
it(`should return "${ JSON.stringify(result) }" from "${ text }"`, () => {
- assert.deepEqual(result, mentionsParser.getChannelMentions(text));
+ expect(result).to.be.deep.equal(mentionsParser.getChannelMentions(text));
});
});
it.skip('should return without the "." from "#general."', () => {
- assert.deepEqual(result, mentionsParser.getUserMentions('#general.'));
+ expect(result).to.be.deep.equal(mentionsParser.getUserMentions('#general.'));
});
it.skip('should return without the "_" from "#general_"', () => {
- assert.deepEqual(result, mentionsParser.getUserMentions('#general_'));
+ expect(result).to.be.deep.equal(mentionsParser.getUserMentions('#general_'));
});
it.skip('should return without the "-" from "#general."', () => {
- assert.deepEqual(result, mentionsParser.getUserMentions('#general-'));
+ expect(result).to.be.deep.equal(mentionsParser.getUserMentions('#general-'));
});
});
@@ -178,7 +176,7 @@ describe('Mention', function() {
'hello #general #other, how are you?',
].forEach((text) => {
it(`should return "${ JSON.stringify(result) }" from "${ text }"`, () => {
- assert.deepEqual(result, mentionsParser.getChannelMentions(text));
+ expect(result).to.be.deep.equal(mentionsParser.getChannelMentions(text));
});
});
});
@@ -189,7 +187,7 @@ describe('Mention', function() {
'http://localhost/#general',
].forEach((text) => {
it(`should return nothing from "${ text }"`, () => {
- assert.deepEqual(result, mentionsParser.getChannelMentions(text));
+ expect(result).to.be.deep.equal(mentionsParser.getChannelMentions(text));
});
});
});
@@ -200,7 +198,7 @@ describe('Mention', function() {
'http://localhost/#general #general',
].forEach((text) => {
it(`should return "${ JSON.stringify(result) }" from "${ text }"`, () => {
- assert.deepEqual(result, mentionsParser.getChannelMentions(text));
+ expect(result).to.be.deep.equal(mentionsParser.getChannelMentions(text));
});
});
});
@@ -216,29 +214,29 @@ describe('replace methods', function() {
describe('replaceUsers', () => {
it('should render for @all', () => {
const result = mentionsParser.replaceUsers('@all', message, 'me');
- assert.equal(result, 'all');
+ expect(result).to.be.equal('all');
});
const str2 = 'rocket.cat';
it(`should render for "@${ str2 }"`, () => {
const result = mentionsParser.replaceUsers(`@${ str2 }`, message, 'me');
- assert.equal(result, `${ str2 }`);
+ expect(result).to.be.equal(`${ str2 }`);
});
it(`should render for "hello ${ str2 }"`, () => {
const result = mentionsParser.replaceUsers(`hello @${ str2 }`, message, 'me');
- assert.equal(result, `hello ${ str2 }`);
+ expect(result).to.be.equal(`hello ${ str2 }`);
});
it('should render for unknow/private user "hello @unknow"', () => {
const result = mentionsParser.replaceUsers('hello @unknow', message, 'me');
- assert.equal(result, 'hello @unknow');
+ expect(result).to.be.equal('hello @unknow');
});
it('should render for me', () => {
const result = mentionsParser.replaceUsers('hello @me', message, 'me');
- assert.equal(result, 'hello me');
+ expect(result).to.be.equal('hello me');
});
});
@@ -249,7 +247,7 @@ describe('replace methods', function() {
it('should render for @all', () => {
const result = mentionsParser.replaceUsers('@all', message, 'me');
- assert.equal(result, 'all');
+ expect(result).to.be.equal('all');
});
const str2 = 'rocket.cat';
@@ -257,12 +255,12 @@ describe('replace methods', function() {
it(`should render for "@${ str2 }"`, () => {
const result = mentionsParser.replaceUsers(`@${ str2 }`, message, 'me');
- assert.equal(result, `${ str2Name }`);
+ expect(result).to.be.equal(`${ str2Name }`);
});
it(`should render for "hello @${ str2 }"`, () => {
const result = mentionsParser.replaceUsers(`hello @${ str2 }`, message, 'me');
- assert.equal(result, `hello ${ str2Name }`);
+ expect(result).to.be.equal(`hello ${ str2Name }`);
});
const specialchars = 'specialchars';
@@ -270,46 +268,46 @@ describe('replace methods', function() {
it(`should escape special characters in "hello @${ specialchars }"`, () => {
const result = mentionsParser.replaceUsers(`hello @${ specialchars }`, message, 'me');
- assert.equal(result, `hello ${ specialcharsName }`);
+ expect(result).to.be.equal(`hello ${ specialcharsName }`);
});
it(`should render for "hello
@${ str2 }
"`, () => {
const result = mentionsParser.replaceUsers(`hello
@${ str2 }
`, message, 'me');
- assert.equal(result, `hello
${ str2Name }
`);
+ expect(result).to.be.equal(`hello
${ str2Name }
`);
});
it('should render for unknow/private user "hello @unknow"', () => {
const result = mentionsParser.replaceUsers('hello @unknow', message, 'me');
- assert.equal(result, 'hello @unknow');
+ expect(result).to.be.equal('hello @unknow');
});
it('should render for me', () => {
const result = mentionsParser.replaceUsers('hello @me', message, 'me');
- assert.equal(result, 'hello Me');
+ expect(result).to.be.equal('hello Me');
});
});
describe('replaceChannels', () => {
it('should render for #general', () => {
const result = mentionsParser.replaceChannels('#general', message);
- assert.equal('#general', result);
+ expect('<).to.be.equal(class="mention-link mention-link--room" data-channel="42">#general', result);
});
const str2 = '#rocket.cat';
it(`should render for ${ str2 }`, () => {
const result = mentionsParser.replaceChannels(str2, message);
- assert.equal(result, `${ str2 }`);
+ expect(result).to.be.equal(`${ str2 }`);
});
it(`should render for "hello ${ str2 }"`, () => {
const result = mentionsParser.replaceChannels(`hello ${ str2 }`, message);
- assert.equal(result, `hello ${ str2 }`);
+ expect(result).to.be.equal(`hello ${ str2 }`);
});
it('should render for unknow/private channel "hello #unknow"', () => {
const result = mentionsParser.replaceChannels('hello #unknow', message);
- assert.equal(result, 'hello #unknow');
+ expect(result).to.be.equal('hello #unknow');
});
});
@@ -317,25 +315,25 @@ describe('replace methods', function() {
it('should render for #general', () => {
message.html = '#general';
const result = mentionsParser.parse(message, 'me');
- assert.equal(result.html, '#general');
+ expect(result.html).to.be.equal('#general');
});
it('should render for "#general and @rocket.cat', () => {
message.html = '#general and @rocket.cat';
const result = mentionsParser.parse(message, 'me');
- assert.equal(result.html, '#general and rocket.cat');
+ expect(result.html).to.be.equal('#general and rocket.cat');
});
it('should render for "', () => {
message.html = '';
const result = mentionsParser.parse(message, 'me');
- assert.equal(result.html, '');
+ expect(result.html).to.be.equal('');
});
it('should render for "simple text', () => {
message.html = 'simple text';
const result = mentionsParser.parse(message, 'me');
- assert.equal(result.html, 'simple text');
+ expect(result.html).to.be.equal('simple text');
});
});
@@ -347,25 +345,25 @@ describe('replace methods', function() {
it('should render for #general', () => {
message.html = '#general';
const result = mentionsParser.parse(message, 'me');
- assert.equal(result.html, '#general');
+ expect(result.html).to.be.equal('#general');
});
it('should render for "#general and @rocket.cat', () => {
message.html = '#general and @rocket.cat';
const result = mentionsParser.parse(message, 'me');
- assert.equal(result.html, '#general and Rocket.Cat');
+ expect(result.html).to.be.equal('#general and Rocket.Cat');
});
it('should render for "', () => {
message.html = '';
const result = mentionsParser.parse(message, 'me');
- assert.equal(result.html, '');
+ expect(result.html).to.be.equal('');
});
it('should render for "simple text', () => {
message.html = 'simple text';
const result = mentionsParser.parse(message, 'me');
- assert.equal(result.html, 'simple text');
+ expect(result.html).to.be.equal('simple text');
});
});
});
diff --git a/app/mentions/tests/server.tests.js b/app/mentions/tests/server.tests.js
index 30bc07564984..a1a77bac3058 100644
--- a/app/mentions/tests/server.tests.js
+++ b/app/mentions/tests/server.tests.js
@@ -1,6 +1,4 @@
-/* eslint-env mocha */
-import 'babel-polyfill';
-import assert from 'assert';
+import { expect } from 'chai';
import MentionsServer from '../server/Mentions';
@@ -43,7 +41,7 @@ describe('Mention Server', () => {
};
const expected = [];
const result = mention.getUsersByMentions(message);
- assert.deepEqual(expected, result);
+ expect(expected).to.be.deep.equal(result);
});
});
describe('for one user', () => {
@@ -69,7 +67,7 @@ describe('Mention Server', () => {
username: 'all',
}];
const result = mention.getUsersByMentions(message);
- assert.deepEqual(expected, result);
+ expect(expected).to.be.deep.equal(result);
});
it('should return "here"', () => {
const message = {
@@ -80,7 +78,7 @@ describe('Mention Server', () => {
username: 'here',
}];
const result = mention.getUsersByMentions(message);
- assert.deepEqual(expected, result);
+ expect(expected).to.be.deep.equal(result);
});
it('should return "rocket.cat"', () => {
const message = {
@@ -91,7 +89,7 @@ describe('Mention Server', () => {
username: 'rocket.cat',
}];
const result = mention.getUsersByMentions(message);
- assert.deepEqual(expected, result);
+ expect(expected).to.be.deep.equal(result);
});
});
describe('for two user', () => {
@@ -107,7 +105,7 @@ describe('Mention Server', () => {
username: 'here',
}];
const result = mention.getUsersByMentions(message);
- assert.deepEqual(expected, result);
+ expect(expected).to.be.deep.equal(result);
});
it('should return "here and rocket.cat"', () => {
const message = {
@@ -121,7 +119,7 @@ describe('Mention Server', () => {
username: 'rocket.cat',
}];
const result = mention.getUsersByMentions(message);
- assert.deepEqual(expected, result);
+ expect(expected).to.be.deep.equal(result);
});
it('should return "here, rocket.cat, jon"', () => {
@@ -139,7 +137,7 @@ describe('Mention Server', () => {
username: 'jon',
}];
const result = mention.getUsersByMentions(message);
- assert.deepEqual(expected, result);
+ expect(expected).to.be.deep.equal(result);
});
});
@@ -150,7 +148,7 @@ describe('Mention Server', () => {
};
const expected = [];
const result = mention.getUsersByMentions(message);
- assert.deepEqual(expected, result);
+ expect(expected).to.be.deep.equal(result);
});
});
});
@@ -164,7 +162,7 @@ describe('Mention Server', () => {
name: 'general',
}];
const result = mention.getChannelbyMentions(message);
- assert.deepEqual(result, expected);
+ expect(result).to.be.deep.equal(expected);
});
it('should return nothing"', () => {
const message = {
@@ -172,7 +170,7 @@ describe('Mention Server', () => {
};
const expected = [];
const result = mention.getChannelbyMentions(message);
- assert.deepEqual(result, expected);
+ expect(result).to.be.deep.equal(expected);
});
});
describe('execute', () => {
@@ -185,7 +183,7 @@ describe('Mention Server', () => {
name: 'general',
}];
const result = mention.getChannelbyMentions(message);
- assert.deepEqual(result, expected);
+ expect(result).to.be.deep.equal(expected);
});
it('should return nothing"', () => {
const message = {
@@ -197,7 +195,7 @@ describe('Mention Server', () => {
channels: [],
};
const result = mention.execute(message);
- assert.deepEqual(result, expected);
+ expect(result).to.be.deep.equal(expected);
});
});
@@ -207,13 +205,13 @@ describe('Mention Server', () => {
describe('constant', () => {
it('should return the informed value', () => {
mention.messageMaxAll = 4;
- assert.deepEqual(mention.messageMaxAll, 4);
+ expect(mention.messageMaxAll).to.be.deep.equal(4);
});
});
describe('function', () => {
it('should return the informed value', () => {
mention.messageMaxAll = () => 4;
- assert.deepEqual(mention.messageMaxAll, 4);
+ expect(mention.messageMaxAll).to.be.deep.equal(4);
});
});
});
@@ -222,13 +220,13 @@ describe('Mention Server', () => {
describe('constant', () => {
it('should return the informed value', () => {
mention.getUsers = 4;
- assert.deepEqual(mention.getUsers(), 4);
+ expect(mention.getUsers()).to.be.deep.equal(4);
});
});
describe('function', () => {
it('should return the informed value', () => {
mention.getUsers = () => 4;
- assert.deepEqual(mention.getUsers(), 4);
+ expect(mention.getUsers()).to.be.deep.equal(4);
});
});
});
@@ -237,13 +235,13 @@ describe('Mention Server', () => {
describe('constant', () => {
it('should return the informed value', () => {
mention.getChannels = 4;
- assert.deepEqual(mention.getChannels(), 4);
+ expect(mention.getChannels()).to.be.deep.equal(4);
});
});
describe('function', () => {
it('should return the informed value', () => {
mention.getChannels = () => 4;
- assert.deepEqual(mention.getChannels(), 4);
+ expect(mention.getChannels()).to.be.deep.equal(4);
});
});
});
@@ -252,13 +250,13 @@ describe('Mention Server', () => {
describe('constant', () => {
it('should return the informed value', () => {
mention.getChannel = true;
- assert.deepEqual(mention.getChannel(), true);
+ expect(mention.getChannel()).to.be.deep.equal(true);
});
});
describe('function', () => {
it('should return the informed value', () => {
mention.getChannel = () => true;
- assert.deepEqual(mention.getChannel(), true);
+ expect(mention.getChannel()).to.be.deep.equal(true);
});
});
});
diff --git a/app/meteor-accounts-saml/server/lib/SAML.ts b/app/meteor-accounts-saml/server/lib/SAML.ts
index 5de64393352a..a0c60ff1d3a5 100644
--- a/app/meteor-accounts-saml/server/lib/SAML.ts
+++ b/app/meteor-accounts-saml/server/lib/SAML.ts
@@ -8,7 +8,8 @@ import fiber from 'fibers';
import { escapeRegExp, escapeHTML } from '@rocket.chat/string-helpers';
import { settings } from '../../../settings/server';
-import { Users, Rooms, CredentialTokens } from '../../../models/server';
+import { Users, Rooms } from '../../../models/server';
+import { CredentialTokens } from '../../../models/server/raw';
import { IUser } from '../../../../definition/IUser';
import { IIncomingMessage } from '../../../../definition/IIncomingMessage';
import { saveUserIdentity, createRoom, generateUsernameSuggestion, addUserToRoom } from '../../../lib/server/functions';
@@ -55,20 +56,20 @@ export class SAML {
}
}
- public static hasCredential(credentialToken: string): boolean {
- return CredentialTokens.findOneById(credentialToken) != null;
+ public static async hasCredential(credentialToken: string): Promise {
+ return await CredentialTokens.findOneNotExpiredById(credentialToken) != null;
}
- public static retrieveCredential(credentialToken: string): Record | undefined {
+ public static async retrieveCredential(credentialToken: string): Promise | undefined> {
// The credentialToken in all these functions corresponds to SAMLs inResponseTo field and is mandatory to check.
- const data = CredentialTokens.findOneById(credentialToken);
+ const data = await CredentialTokens.findOneNotExpiredById(credentialToken);
if (data) {
return data.userInfo;
}
}
- public static storeCredential(credentialToken: string, loginResult: object): void {
- CredentialTokens.create(credentialToken, loginResult);
+ public static async storeCredential(credentialToken: string, loginResult: {profile: Record}): Promise {
+ await CredentialTokens.create(credentialToken, loginResult);
}
public static insertOrUpdateSAMLUser(userObject: ISAMLUser): {userId: string; token: string} {
@@ -380,7 +381,7 @@ export class SAML {
private static processValidateAction(req: IIncomingMessage, res: ServerResponse, service: IServiceProviderOptions, _samlObject: ISAMLAction): void {
const serviceProvider = new SAMLServiceProvider(service);
SAMLUtils.relayState = req.body.RelayState;
- serviceProvider.validateResponse(req.body.SAMLResponse, (err, profile/* , loggedOut*/) => {
+ serviceProvider.validateResponse(req.body.SAMLResponse, async (err, profile/* , loggedOut*/) => {
try {
if (err) {
SAMLUtils.error(err);
@@ -400,7 +401,7 @@ export class SAML {
profile,
};
- this.storeCredential(credentialToken, loginResult);
+ await this.storeCredential(credentialToken, loginResult);
const url = `${ Meteor.absoluteUrl('home') }?saml_idp_credentialToken=${ credentialToken }`;
res.writeHead(302, {
Location: url,
diff --git a/app/meteor-accounts-saml/server/lib/parsers/Response.ts b/app/meteor-accounts-saml/server/lib/parsers/Response.ts
index 89bbc0758093..f92ba7fedef2 100644
--- a/app/meteor-accounts-saml/server/lib/parsers/Response.ts
+++ b/app/meteor-accounts-saml/server/lib/parsers/Response.ts
@@ -176,7 +176,7 @@ export class ResponseParser {
if (typeof encAssertion !== 'undefined') {
const options = { key: this.serviceProviderOptions.privateKey };
const encData = encAssertion.getElementsByTagNameNS('*', 'EncryptedData')[0];
- xmlenc.decrypt(encData, options, function(err: Error, result: string) {
+ xmlenc.decrypt(encData, options, function(err, result) {
if (err) {
SAMLUtils.error(err);
}
@@ -318,7 +318,7 @@ export class ResponseParser {
if (typeof encSubject !== 'undefined') {
const options = { key: this.serviceProviderOptions.privateKey };
- xmlenc.decrypt(encSubject.getElementsByTagNameNS('*', 'EncryptedData')[0], options, function(err: Error, result: string) {
+ xmlenc.decrypt(encSubject.getElementsByTagNameNS('*', 'EncryptedData')[0], options, (err, result) => {
if (err) {
SAMLUtils.error(err);
}
diff --git a/app/meteor-accounts-saml/server/loginHandler.ts b/app/meteor-accounts-saml/server/loginHandler.ts
index 6b73c7f386ec..edb58716d974 100644
--- a/app/meteor-accounts-saml/server/loginHandler.ts
+++ b/app/meteor-accounts-saml/server/loginHandler.ts
@@ -17,7 +17,7 @@ Accounts.registerLoginHandler('saml', function(loginRequest) {
return undefined;
}
- const loginResult = SAML.retrieveCredential(loginRequest.credentialToken);
+ const loginResult = Promise.await(SAML.retrieveCredential(loginRequest.credentialToken));
SAMLUtils.log({ msg: 'RESULT', loginResult });
if (!loginResult) {
diff --git a/app/meteor-accounts-saml/tests/server.tests.ts b/app/meteor-accounts-saml/tests/server.tests.ts
index d8ca3ba2f702..2b64134e22a9 100644
--- a/app/meteor-accounts-saml/tests/server.tests.ts
+++ b/app/meteor-accounts-saml/tests/server.tests.ts
@@ -1,7 +1,4 @@
-/* eslint-env mocha */
-import 'babel-polyfill';
-
-import chai from 'chai';
+import { expect } from 'chai';
import '../../lib/tests/server.mocks.js';
import { AuthorizeRequest } from '../server/lib/generators/AuthorizeRequest';
@@ -38,9 +35,6 @@ import {
privateKeyCert,
privateKey,
} from './data';
-import '../../../definition/xml-encryption';
-
-const { expect } = chai;
describe('SAML', () => {
describe('[AuthorizeRequest]', () => {
diff --git a/app/metrics/server/lib/collectMetrics.js b/app/metrics/server/lib/collectMetrics.js
index 98267f9ca855..f72437df2f04 100644
--- a/app/metrics/server/lib/collectMetrics.js
+++ b/app/metrics/server/lib/collectMetrics.js
@@ -10,7 +10,7 @@ import { Facts } from 'meteor/facts-base';
import { Info, getOplogInfo } from '../../../utils/server';
import { getControl } from '../../../../server/lib/migrations';
import { settings } from '../../../settings/server';
-import { Statistics } from '../../../models/server';
+import { Statistics } from '../../../models/server/raw';
import { SystemLogger } from '../../../../server/lib/logger/system';
import { metrics } from './metrics';
import { getAppsStatistics } from '../../../statistics/server/lib/getAppsStatistics';
@@ -42,7 +42,7 @@ const setPrometheusData = async () => {
const oplogQueue = getOplogInfo().mongo._oplogHandle?._entryQueue?.length || 0;
metrics.oplogQueue.set(oplogQueue);
- const statistics = Statistics.findLast();
+ const statistics = await Statistics.findLast();
if (!statistics) {
return;
}
diff --git a/app/models/server/index.js b/app/models/server/index.js
index 8c9094d654ef..5507cf53f0aa 100644
--- a/app/models/server/index.js
+++ b/app/models/server/index.js
@@ -1,31 +1,11 @@
import { Base } from './models/_Base';
import { BaseDb } from './models/_BaseDb';
-import Avatars from './models/Avatars';
-import ExportOperations from './models/ExportOperations';
import Messages from './models/Messages';
-import Reports from './models/Reports';
import Rooms from './models/Rooms';
import Settings from './models/Settings';
import Subscriptions from './models/Subscriptions';
-import Uploads from './models/Uploads';
-import UserDataFiles from './models/UserDataFiles';
import Users from './models/Users';
-import Sessions from './models/Sessions';
-import Statistics from './models/Statistics';
-import Permissions from './models/Permissions';
-import Roles from './models/Roles';
-import CustomSounds from './models/CustomSounds';
-import CustomUserStatus from './models/CustomUserStatus';
import Imports from './models/Imports';
-import Integrations from './models/Integrations';
-import IntegrationHistory from './models/IntegrationHistory';
-import Invites from './models/Invites';
-import CredentialTokens from './models/CredentialTokens';
-import EmojiCustom from './models/EmojiCustom';
-import OAuthApps from './models/OAuthApps';
-import OEmbedCache from './models/OEmbedCache';
-import SmarshHistory from './models/SmarshHistory';
-import WebdavAccounts from './models/WebdavAccounts';
import LivechatCustomField from './models/LivechatCustomField';
import LivechatDepartment from './models/LivechatDepartment';
import LivechatDepartmentAgents from './models/LivechatDepartmentAgents';
@@ -35,50 +15,24 @@ import LivechatTrigger from './models/LivechatTrigger';
import LivechatVisitors from './models/LivechatVisitors';
import LivechatAgentActivity from './models/LivechatAgentActivity';
import LivechatInquiry from './models/LivechatInquiry';
-import ReadReceipts from './models/ReadReceipts';
import LivechatExternalMessage from './models/LivechatExternalMessages';
import OmnichannelQueue from './models/OmnichannelQueue';
-import Analytics from './models/Analytics';
-import EmailInbox from './models/EmailInbox';
import ImportData from './models/ImportData';
export { AppsLogsModel } from './models/apps-logs-model';
export { AppsPersistenceModel } from './models/apps-persistence-model';
export { AppsModel } from './models/apps-model';
-export { FederationDNSCache } from './models/FederationDNSCache';
export { FederationRoomEvents } from './models/FederationRoomEvents';
-export { FederationKeys } from './models/FederationKeys';
-export { FederationServers } from './models/FederationServers';
export {
Base,
BaseDb,
- Avatars,
- ExportOperations,
Messages,
- Reports,
Rooms,
Settings,
Subscriptions,
- Uploads,
- UserDataFiles,
Users,
- Sessions,
- Statistics,
- Permissions,
- Roles,
- CustomSounds,
- CustomUserStatus,
Imports,
- Integrations,
- IntegrationHistory,
- Invites,
- CredentialTokens,
- EmojiCustom,
- OAuthApps,
- OEmbedCache,
- SmarshHistory,
- WebdavAccounts,
LivechatCustomField,
LivechatDepartment,
LivechatDepartmentAgents,
@@ -87,11 +41,8 @@ export {
LivechatTrigger,
LivechatVisitors,
LivechatAgentActivity,
- ReadReceipts,
LivechatExternalMessage,
LivechatInquiry,
- Analytics,
OmnichannelQueue,
- EmailInbox,
ImportData,
};
diff --git a/app/models/server/models/Analytics.js b/app/models/server/models/Analytics.js
deleted file mode 100644
index c521fda8923e..000000000000
--- a/app/models/server/models/Analytics.js
+++ /dev/null
@@ -1,11 +0,0 @@
-import { Base } from './_Base';
-
-export class Analytics extends Base {
- constructor() {
- super('analytics');
- this.tryEnsureIndex({ date: 1 });
- this.tryEnsureIndex({ 'room._id': 1, date: 1 }, { unique: true });
- }
-}
-
-export default new Analytics();
diff --git a/app/models/server/models/Avatars.js b/app/models/server/models/Avatars.js
deleted file mode 100644
index b0e7e8cb5da1..000000000000
--- a/app/models/server/models/Avatars.js
+++ /dev/null
@@ -1,92 +0,0 @@
-import _ from 'underscore';
-import s from 'underscore.string';
-import { InstanceStatus } from 'meteor/konecty:multiple-instances-status';
-
-import { Base } from './_Base';
-
-export class Avatars extends Base {
- constructor() {
- super('avatars');
-
- this.model.before.insert((userId, doc) => {
- doc.instanceId = InstanceStatus.id();
- });
-
- this.tryEnsureIndex({ name: 1 }, { sparse: true });
- this.tryEnsureIndex({ rid: 1 }, { sparse: true });
- }
-
- insertAvatarFileInit(name, userId, store, file, extra) {
- const fileData = {
- _id: name,
- name,
- userId,
- store,
- complete: false,
- uploading: true,
- progress: 0,
- extension: s.strRightBack(file.name, '.'),
- uploadedAt: new Date(),
- };
-
- _.extend(fileData, file, extra);
-
- return this.insertOrUpsert(fileData);
- }
-
- updateFileComplete(fileId, userId, file) {
- if (!fileId) {
- return;
- }
-
- const filter = {
- _id: fileId,
- userId,
- };
-
- const update = {
- $set: {
- complete: true,
- uploading: false,
- progress: 1,
- },
- };
-
- update.$set = _.extend(file, update.$set);
-
- if (this.model.direct && this.model.direct.update) {
- return this.model.direct.update(filter, update);
- }
- return this.update(filter, update);
- }
-
- findOneByName(name) {
- return this.findOne({ name });
- }
-
- findOneByRoomId(rid) {
- return this.findOne({ rid });
- }
-
- updateFileNameById(fileId, name) {
- const filter = { _id: fileId };
- const update = {
- $set: {
- name,
- },
- };
- if (this.model.direct && this.model.direct.update) {
- return this.model.direct.update(filter, update);
- }
- return this.update(filter, update);
- }
-
- deleteFile(fileId) {
- if (this.model.direct && this.model.direct.remove) {
- return this.model.direct.remove({ _id: fileId });
- }
- return this.remove({ _id: fileId });
- }
-}
-
-export default new Avatars();
diff --git a/app/models/server/models/CredentialTokens.js b/app/models/server/models/CredentialTokens.js
deleted file mode 100644
index 7659538e032e..000000000000
--- a/app/models/server/models/CredentialTokens.js
+++ /dev/null
@@ -1,32 +0,0 @@
-import { Base } from './_Base';
-
-export class CredentialTokens extends Base {
- constructor() {
- super('credential_tokens');
-
- this.tryEnsureIndex({ expireAt: 1 }, { sparse: 1, expireAfterSeconds: 0 });
- }
-
- create(_id, userInfo) {
- const validForMilliseconds = 60000; // Valid for 60 seconds
- const token = {
- _id,
- userInfo,
- expireAt: new Date(Date.now() + validForMilliseconds),
- };
-
- this.insert(token);
- return token;
- }
-
- findOneById(_id) {
- const query = {
- _id,
- expireAt: { $gt: new Date() },
- };
-
- return this.findOne(query);
- }
-}
-
-export default new CredentialTokens();
diff --git a/app/models/server/models/CustomSounds.js b/app/models/server/models/CustomSounds.js
deleted file mode 100644
index b9971b954229..000000000000
--- a/app/models/server/models/CustomSounds.js
+++ /dev/null
@@ -1,56 +0,0 @@
-import { Base } from './_Base';
-
-class CustomSounds extends Base {
- constructor() {
- super('custom_sounds');
-
- this.tryEnsureIndex({ name: 1 });
- }
-
- // find one
- findOneById(_id, options) {
- return this.findOne(_id, options);
- }
-
- // find
- findByName(name, options) {
- const query = {
- name,
- };
-
- return this.find(query, options);
- }
-
- findByNameExceptId(name, except, options) {
- const query = {
- _id: { $nin: [except] },
- name,
- };
-
- return this.find(query, options);
- }
-
- // update
- setName(_id, name) {
- const update = {
- $set: {
- name,
- },
- };
-
- return this.update({ _id }, update);
- }
-
- // INSERT
- create(data) {
- return this.insert(data);
- }
-
-
- // REMOVE
- removeById(_id) {
- return this.remove(_id);
- }
-}
-
-export default new CustomSounds();
diff --git a/app/models/server/models/CustomUserStatus.js b/app/models/server/models/CustomUserStatus.js
deleted file mode 100644
index eb3a586da6ba..000000000000
--- a/app/models/server/models/CustomUserStatus.js
+++ /dev/null
@@ -1,71 +0,0 @@
-import { Base } from './_Base';
-
-class CustomUserStatus extends Base {
- constructor() {
- super('custom_user_status');
-
- this.tryEnsureIndex({ name: 1 });
- }
-
- // find one
- findOneById(_id, options) {
- return this.findOne(_id, options);
- }
-
- // find one by name
- findOneByName(name, options) {
- return this.findOne({ name }, options);
- }
-
- // find
- findByName(name, options) {
- const query = {
- name,
- };
-
- return this.find(query, options);
- }
-
- findByNameExceptId(name, except, options) {
- const query = {
- _id: { $nin: [except] },
- name,
- };
-
- return this.find(query, options);
- }
-
- // update
- setName(_id, name) {
- const update = {
- $set: {
- name,
- },
- };
-
- return this.update({ _id }, update);
- }
-
- setStatusType(_id, statusType) {
- const update = {
- $set: {
- statusType,
- },
- };
-
- return this.update({ _id }, update);
- }
-
- // INSERT
- create(data) {
- return this.insert(data);
- }
-
-
- // REMOVE
- removeById(_id) {
- return this.remove(_id);
- }
-}
-
-export default new CustomUserStatus();
diff --git a/app/models/server/models/EmailInbox.js b/app/models/server/models/EmailInbox.js
deleted file mode 100644
index 490628be3383..000000000000
--- a/app/models/server/models/EmailInbox.js
+++ /dev/null
@@ -1,27 +0,0 @@
-import { Base } from './_Base';
-
-export class EmailInbox extends Base {
- constructor() {
- super('email_inbox');
-
- this.tryEnsureIndex({ email: 1 }, { unique: true });
- }
-
- findOneById(_id, options) {
- return this.findOne(_id, options);
- }
-
- create(data) {
- return this.insert(data);
- }
-
- updateById(_id, data) {
- return this.update({ _id }, data);
- }
-
- removeById(_id) {
- return this.remove(_id);
- }
-}
-
-export default new EmailInbox();
diff --git a/app/models/server/models/EmojiCustom.js b/app/models/server/models/EmojiCustom.js
deleted file mode 100644
index d0cd7d7bc4cb..000000000000
--- a/app/models/server/models/EmojiCustom.js
+++ /dev/null
@@ -1,91 +0,0 @@
-import { Base } from './_Base';
-
-class EmojiCustom extends Base {
- constructor() {
- super('custom_emoji');
-
- this.tryEnsureIndex({ name: 1 });
- this.tryEnsureIndex({ aliases: 1 });
- this.tryEnsureIndex({ extension: 1 });
- }
-
- // find one
- findOneById(_id, options) {
- return this.findOne(_id, options);
- }
-
- // find
- findByNameOrAlias(emojiName, options) {
- let name = emojiName;
-
- if (typeof emojiName === 'string') {
- name = emojiName.replace(/:/g, '');
- }
-
- const query = {
- $or: [
- { name },
- { aliases: name },
- ],
- };
-
- return this.find(query, options);
- }
-
- findByNameOrAliasExceptID(name, except, options) {
- const query = {
- _id: { $nin: [except] },
- $or: [
- { name },
- { aliases: name },
- ],
- };
-
- return this.find(query, options);
- }
-
-
- // update
- setName(_id, name) {
- const update = {
- $set: {
- name,
- },
- };
-
- return this.update({ _id }, update);
- }
-
- setAliases(_id, aliases) {
- const update = {
- $set: {
- aliases,
- },
- };
-
- return this.update({ _id }, update);
- }
-
- setExtension(_id, extension) {
- const update = {
- $set: {
- extension,
- },
- };
-
- return this.update({ _id }, update);
- }
-
- // INSERT
- create(data) {
- return this.insert(data);
- }
-
-
- // REMOVE
- removeById(_id) {
- return this.remove(_id);
- }
-}
-
-export default new EmojiCustom();
diff --git a/app/models/server/models/ExportOperations.js b/app/models/server/models/ExportOperations.js
deleted file mode 100644
index fb70d38925ca..000000000000
--- a/app/models/server/models/ExportOperations.js
+++ /dev/null
@@ -1,108 +0,0 @@
-import _ from 'underscore';
-
-import { Base } from './_Base';
-
-export class ExportOperations extends Base {
- constructor() {
- super('export_operations');
-
- this.tryEnsureIndex({ userId: 1 });
- this.tryEnsureIndex({ status: 1 });
- }
-
- // FIND
- findById(id) {
- const query = { _id: id };
-
- return this.find(query);
- }
-
- findLastOperationByUser(userId, fullExport = false, options = {}) {
- const query = {
- userId,
- fullExport,
- };
-
- options.sort = { createdAt: -1 };
- return this.findOne(query, options);
- }
-
- findPendingByUser(userId, options) {
- const query = {
- userId,
- status: {
- $nin: ['completed', 'skipped'],
- },
- };
-
- return this.find(query, options);
- }
-
- findAllPending(options) {
- const query = {
- status: { $nin: ['completed', 'skipped'] },
- };
-
- return this.find(query, options);
- }
-
- findOnePending(options) {
- const query = {
- status: { $nin: ['completed', 'skipped'] },
- };
-
- return this.findOne(query, options);
- }
-
- findAllPendingBeforeMyRequest(requestDay, options) {
- const query = {
- status: { $nin: ['completed', 'skipped'] },
- createdAt: { $lt: requestDay },
- };
-
- return this.find(query, options);
- }
-
- // UPDATE
- updateOperation(data) {
- const update = {
- $set: {
- roomList: data.roomList,
- status: data.status,
- fileList: data.fileList,
- generatedFile: data.generatedFile,
- fileId: data.fileId,
- userNameTable: data.userNameTable,
- userData: data.userData,
- generatedUserFile: data.generatedUserFile,
- generatedAvatar: data.generatedAvatar,
- exportPath: data.exportPath,
- assetsPath: data.assetsPath,
- },
- };
-
- return this.update(data._id, update);
- }
-
-
- // INSERT
- create(data) {
- const exportOperation = {
- createdAt: new Date(),
- };
-
- _.extend(exportOperation, data);
-
- this.insert(exportOperation);
-
- return exportOperation._id;
- }
-
-
- // REMOVE
- removeById(_id) {
- return this.remove(_id);
- }
-}
-
-export default new ExportOperations();
diff --git a/app/models/server/models/FederationDNSCache.js b/app/models/server/models/FederationDNSCache.js
deleted file mode 100644
index 155deed53b95..000000000000
--- a/app/models/server/models/FederationDNSCache.js
+++ /dev/null
@@ -1,13 +0,0 @@
-import { Base } from './_Base';
-
-class FederationDNSCacheModel extends Base {
- constructor() {
- super('federation_dns_cache');
- }
-
- findOneByDomain(domain) {
- return this.findOne({ domain });
- }
-}
-
-export const FederationDNSCache = new FederationDNSCacheModel();
diff --git a/app/models/server/models/FederationKeys.js b/app/models/server/models/FederationKeys.js
deleted file mode 100644
index 188f7cdc434e..000000000000
--- a/app/models/server/models/FederationKeys.js
+++ /dev/null
@@ -1,58 +0,0 @@
-import NodeRSA from 'node-rsa';
-
-import { Base } from './_Base';
-
-class FederationKeysModel extends Base {
- constructor() {
- super('federation_keys');
- }
-
- getKey(type) {
- const keyResource = this.findOne({ type });
-
- if (!keyResource) { return null; }
-
- return keyResource.key;
- }
-
- loadKey(keyData, type) {
- return new NodeRSA(keyData, `pkcs8-${ type }-pem`);
- }
-
- generateKeys() {
- const key = new NodeRSA({ b: 512 });
-
- key.generateKeyPair();
-
- this.update({ type: 'private' }, { type: 'private', key: key.exportKey('pkcs8-private-pem').replace(/\n|\r/g, '') }, { upsert: true });
-
- this.update({ type: 'public' }, { type: 'public', key: key.exportKey('pkcs8-public-pem').replace(/\n|\r/g, '') }, { upsert: true });
-
- return {
- privateKey: this.getPrivateKey(),
- publicKey: this.getPublicKey(),
- };
- }
-
- getPrivateKey() {
- const keyData = this.getKey('private');
-
- return keyData && this.loadKey(keyData, 'private');
- }
-
- getPrivateKeyString() {
- return this.getKey('private');
- }
-
- getPublicKey() {
- const keyData = this.getKey('public');
-
- return keyData && this.loadKey(keyData, 'public');
- }
-
- getPublicKeyString() {
- return this.getKey('public');
- }
-}
-
-export const FederationKeys = new FederationKeysModel();
diff --git a/app/models/server/models/FederationServers.js b/app/models/server/models/FederationServers.js
deleted file mode 100644
index 9daf20d5a128..000000000000
--- a/app/models/server/models/FederationServers.js
+++ /dev/null
@@ -1,26 +0,0 @@
-import { Base } from './_Base';
-import { Users } from '../raw';
-
-class FederationServersModel extends Base {
- constructor() {
- super('federation_servers');
-
- this.tryEnsureIndex({ domain: 1 });
- }
-
- async refreshServers() {
- const domains = await Users.getDistinctFederationDomains();
-
- domains.forEach((domain) => {
- this.update({ domain }, {
- $setOnInsert: {
- domain,
- },
- }, { upsert: true });
- });
-
- this.remove({ domain: { $nin: domains } });
- }
-}
-
-export const FederationServers = new FederationServersModel();
diff --git a/app/models/server/models/InstanceStatus.js b/app/models/server/models/InstanceStatus.js
deleted file mode 100644
index 344381e44266..000000000000
--- a/app/models/server/models/InstanceStatus.js
+++ /dev/null
@@ -1,7 +0,0 @@
-import { InstanceStatus } from 'meteor/konecty:multiple-instances-status';
-
-import { Base } from './_Base';
-
-export class InstanceStatusModel extends Base {}
-
-export default new InstanceStatusModel(InstanceStatus.getCollection(), { preventSetUpdatedAt: true });
diff --git a/app/models/server/models/IntegrationHistory.js b/app/models/server/models/IntegrationHistory.js
deleted file mode 100644
index 817deae0d789..000000000000
--- a/app/models/server/models/IntegrationHistory.js
+++ /dev/null
@@ -1,43 +0,0 @@
-import { Meteor } from 'meteor/meteor';
-
-import { Base } from './_Base';
-
-export class IntegrationHistory extends Base {
- constructor() {
- super('integration_history');
- }
-
- findByType(type, options) {
- if (type !== 'outgoing-webhook' || type !== 'incoming-webhook') {
- throw new Meteor.Error('invalid-integration-type');
- }
-
- return this.find({ type }, options);
- }
-
- findByIntegrationId(id, options) {
- return this.find({ 'integration._id': id }, options);
- }
-
- findByIntegrationIdAndCreatedBy(id, creatorId, options) {
- return this.find({ 'integration._id': id, 'integration._createdBy._id': creatorId }, options);
- }
-
- findOneByIntegrationIdAndHistoryId(integrationId, historyId) {
- return this.findOne({ 'integration._id': integrationId, _id: historyId });
- }
-
- findByEventName(event, options) {
- return this.find({ event }, options);
- }
-
- findFailed(options) {
- return this.find({ error: true }, options);
- }
-
- removeByIntegrationId(integrationId) {
- return this.remove({ 'integration._id': integrationId });
- }
-}
-
-export default new IntegrationHistory();
diff --git a/app/models/server/models/Integrations.js b/app/models/server/models/Integrations.js
deleted file mode 100644
index ffbf40c1dcce..000000000000
--- a/app/models/server/models/Integrations.js
+++ /dev/null
@@ -1,31 +0,0 @@
-import { Meteor } from 'meteor/meteor';
-
-import { Base } from './_Base';
-
-export class Integrations extends Base {
- constructor() {
- super('integrations');
-
- this.tryEnsureIndex({ type: 1 });
- }
-
- findByType(type, options) {
- if (type !== 'webhook-incoming' && type !== 'webhook-outgoing') {
- throw new Meteor.Error('invalid-type-to-find');
- }
-
- return this.find({ type }, options);
- }
-
- disableByUserId(userId) {
- return this.update({ userId }, { $set: { enabled: false } }, { multi: true });
- }
-
- updateRoomName(oldRoomName, newRoomName) {
- const hashedOldRoomName = `#${ oldRoomName }`;
- const hashedNewRoomName = `#${ newRoomName }`;
- return this.update({ channel: hashedOldRoomName }, { $set: { 'channel.$': hashedNewRoomName } }, { multi: true });
- }
-}
-
-export default new Integrations();
diff --git a/app/models/server/models/Invites.js b/app/models/server/models/Invites.js
deleted file mode 100644
index 517c1780f0ba..000000000000
--- a/app/models/server/models/Invites.js
+++ /dev/null
@@ -1,51 +0,0 @@
-import { Base } from './_Base';
-
-class Invites extends Base {
- constructor() {
- super('invites');
- }
-
- findOneByUserRoomMaxUsesAndExpiration(userId, rid, maxUses, daysToExpire) {
- const query = {
- rid,
- userId,
- days: daysToExpire,
- maxUses,
- };
-
- if (daysToExpire > 0) {
- query.expires = {
- $gt: new Date(),
- };
- }
-
- if (maxUses > 0) {
- query.uses = {
- $lt: maxUses,
- };
- }
-
- return this.findOne(query);
- }
-
- // INSERT
- create(data) {
- return this.insert(data);
- }
-
- // REMOVE
- removeById(_id) {
- return this.remove({ _id });
- }
-
- // UPDATE
- increaseUsageById(_id, uses = 1) {
- return this.update({ _id }, {
- $inc: {
- uses,
- },
- });
- }
-}
-
-export default new Invites();
diff --git a/app/models/server/models/LivechatRooms.js b/app/models/server/models/LivechatRooms.js
index 13b4c7984769..d899284b4686 100644
--- a/app/models/server/models/LivechatRooms.js
+++ b/app/models/server/models/LivechatRooms.js
@@ -21,6 +21,7 @@ export class LivechatRooms extends Base {
this.tryEnsureIndex({ 'v.token': 1, 'email.thread': 1 }, { sparse: true });
this.tryEnsureIndex({ 'v._id': 1 }, { sparse: true });
this.tryEnsureIndex({ t: 1, departmentId: 1, closedAt: 1 }, { partialFilterExpression: { closedAt: { $exists: true } } });
+ this.tryEnsureIndex({ source: 1 }, { sparse: true });
}
findLivechat(filter = {}, offset = 0, limit = 20) {
@@ -280,6 +281,17 @@ export class LivechatRooms extends Base {
return this.findOne(query, options);
}
+ findOneOpenByVisitorTokenAndDepartmentId(visitorToken, departmentId, options) {
+ const query = {
+ t: 'l',
+ open: true,
+ 'v.token': visitorToken,
+ departmentId,
+ };
+
+ return this.findOne(query, options);
+ }
+
findOpenByVisitorTokenAndDepartmentId(visitorToken, departmentId, options) {
const query = {
t: 'l',
@@ -564,6 +576,7 @@ export class LivechatRooms extends Base {
open: '$open',
servedBy: '$servedBy',
metrics: '$metrics',
+ onHold: '$onHold',
},
messagesCount: {
$sum: 1,
@@ -579,6 +592,7 @@ export class LivechatRooms extends Base {
servedBy: '$_id.servedBy',
metrics: '$_id.metrics',
msgs: '$messagesCount',
+ onHold: '$_id.onHold',
},
},
]);
diff --git a/app/models/server/models/NotificationQueue.js b/app/models/server/models/NotificationQueue.js
deleted file mode 100644
index 32eb7524c2c2..000000000000
--- a/app/models/server/models/NotificationQueue.js
+++ /dev/null
@@ -1,14 +0,0 @@
-import { Base } from './_Base';
-
-export class NotificationQueue extends Base {
- constructor() {
- super('notification_queue');
- this.tryEnsureIndex({ uid: 1 });
- this.tryEnsureIndex({ ts: 1 }, { expireAfterSeconds: 2 * 60 * 60 });
- this.tryEnsureIndex({ schedule: 1 }, { sparse: true });
- this.tryEnsureIndex({ sending: 1 }, { sparse: true });
- this.tryEnsureIndex({ error: 1 }, { sparse: true });
- }
-}
-
-export default new NotificationQueue();
diff --git a/app/models/server/models/OAuthApps.js b/app/models/server/models/OAuthApps.js
deleted file mode 100644
index 6aedffb63ae0..000000000000
--- a/app/models/server/models/OAuthApps.js
+++ /dev/null
@@ -1,9 +0,0 @@
-import { Base } from './_Base';
-
-export class OAuthApps extends Base {
- constructor() {
- super('oauth_apps');
- }
-}
-
-export default new OAuthApps();
diff --git a/app/models/server/models/OEmbedCache.js b/app/models/server/models/OEmbedCache.js
deleted file mode 100644
index db4383b9cd34..000000000000
--- a/app/models/server/models/OEmbedCache.js
+++ /dev/null
@@ -1,39 +0,0 @@
-import { Base } from './_Base';
-
-export class OEmbedCache extends Base {
- constructor() {
- super('oembed_cache');
- this.tryEnsureIndex({ updatedAt: 1 });
- }
-
- // FIND ONE
- findOneById(_id, options) {
- const query = {
- _id,
- };
- return this.findOne(query, options);
- }
-
- // INSERT
- createWithIdAndData(_id, data) {
- const record = {
- _id,
- data,
- updatedAt: new Date(),
- };
- record._id = this.insert(record);
- return record;
- }
-
- // REMOVE
- removeAfterDate(date) {
- const query = {
- updatedAt: {
- $lte: date,
- },
- };
- return this.remove(query);
- }
-}
-
-export default new OEmbedCache();
diff --git a/app/models/server/models/Permissions.js b/app/models/server/models/Permissions.js
deleted file mode 100644
index 009f29d37f9d..000000000000
--- a/app/models/server/models/Permissions.js
+++ /dev/null
@@ -1,49 +0,0 @@
-import { Base } from './_Base';
-
-export class Permissions extends Base {
- // FIND
- findByRole(role, options) {
- const query = {
- roles: role,
- };
-
- return this.find(query, options);
- }
-
- findOneById(_id) {
- return this.findOne({ _id });
- }
-
- createOrUpdate(name, roles) {
- const exists = this.findOne({
- _id: name,
- roles,
- }, { fields: { _id: 1 } });
-
- if (exists) {
- return exists._id;
- }
-
- this.upsert({ _id: name }, { $set: { roles } });
- }
-
- create(name, roles) {
- const exists = this.findOneById(name, { fields: { _id: 1 } });
-
- if (exists) {
- return exists._id;
- }
-
- this.upsert({ _id: name }, { $set: { roles } });
- }
-
- addRole(permission, role) {
- this.update({ _id: permission, roles: { $ne: role } }, { $addToSet: { roles: role } });
- }
-
- removeRole(permission, role) {
- this.update({ _id: permission, roles: role }, { $pull: { roles: role } });
- }
-}
-
-export default new Permissions('permissions');
diff --git a/app/models/server/models/ReadReceipts.js b/app/models/server/models/ReadReceipts.js
deleted file mode 100644
index d830f400669f..000000000000
--- a/app/models/server/models/ReadReceipts.js
+++ /dev/null
@@ -1,25 +0,0 @@
-import { Base } from './_Base';
-
-export class ReadReceipts extends Base {
- constructor(...args) {
- super(...args);
-
- this.tryEnsureIndex({
- roomId: 1,
- userId: 1,
- messageId: 1,
- }, {
- unique: 1,
- });
-
- this.tryEnsureIndex({
- messageId: 1,
- });
- }
-
- findByMessageId(messageId) {
- return this.find({ messageId });
- }
-}
-
-export default new ReadReceipts('message_read_receipt');
diff --git a/app/models/server/models/Reports.js b/app/models/server/models/Reports.js
deleted file mode 100644
index 4d2ab019f19b..000000000000
--- a/app/models/server/models/Reports.js
+++ /dev/null
@@ -1,23 +0,0 @@
-import _ from 'underscore';
-
-import { Base } from './_Base';
-
-export class Reports extends Base {
- constructor() {
- super('reports');
- }
-
- createWithMessageDescriptionAndUserId(message, description, userId, extraData) {
- const record = {
- message,
- description,
- ts: new Date(),
- userId,
- };
- _.extend(record, extraData);
- record._id = this.insert(record);
- return record;
- }
-}
-
-export default new Reports();
diff --git a/app/models/server/models/Roles.js b/app/models/server/models/Roles.js
deleted file mode 100644
index e7576191d070..000000000000
--- a/app/models/server/models/Roles.js
+++ /dev/null
@@ -1,134 +0,0 @@
-import { Base } from './_Base';
-import * as Models from '..';
-
-
-export class Roles extends Base {
- constructor(...args) {
- super(...args);
- this.tryEnsureIndex({ name: 1 });
- this.tryEnsureIndex({ scope: 1 });
- }
-
- findUsersInRole(name, scope, options) {
- const role = this.findOneByName(name);
- const roleScope = (role && role.scope) || 'Users';
- const model = Models[roleScope];
-
- return model && model.findUsersInRoles && model.findUsersInRoles(name, scope, options);
- }
-
- isUserInRoles(userId, roles, scope) {
- roles = [].concat(roles);
- return roles.some((roleName) => {
- const role = this.findOneByName(roleName);
- const roleScope = (role && role.scope) || 'Users';
- const model = Models[roleScope];
-
- return model && model.isUserInRole && model.isUserInRole(userId, roleName, scope);
- });
- }
-
- updateById(_id, name, scope, description, mandatory2fa) {
- const queryData = {
- name,
- scope,
- description,
- mandatory2fa,
- };
-
- this.upsert({ _id }, { $set: queryData });
- }
-
- createWithRandomId(name, scope = 'Users', description = '', protectedRole = true, mandatory2fa = false) {
- const role = {
- name,
- scope,
- description,
- protected: protectedRole,
- mandatory2fa,
- };
-
- return this.insert(role);
- }
-
- createOrUpdate(name, scope = 'Users', description = '', protectedRole = true, mandatory2fa = false) {
- const queryData = {
- name,
- scope,
- description,
- protected: protectedRole,
- mandatory2fa,
- };
-
- this.upsert({ _id: name }, { $set: queryData });
- }
-
- addUserRoles(userId, roles, scope) {
- roles = [].concat(roles);
- for (const roleName of roles) {
- const role = this.findOneByName(roleName);
- const roleScope = (role && role.scope) || 'Users';
- const model = Models[roleScope];
-
- model && model.addRolesByUserId && model.addRolesByUserId(userId, roleName, scope);
- }
- return true;
- }
-
- removeUserRoles(userId, roles, scope) {
- roles = [].concat(roles);
- for (const roleName of roles) {
- const role = this.findOneByName(roleName);
- const roleScope = (role && role.scope) || 'Users';
- const model = Models[roleScope];
-
- model && model.removeRolesByUserId && model.removeRolesByUserId(userId, roleName, scope);
- }
- return true;
- }
-
- findOneByIdOrName(_idOrName, options) {
- const query = {
- $or: [{
- _id: _idOrName,
- }, {
- name: _idOrName,
- }],
- };
-
- return this.findOne(query, options);
- }
-
- findOneByName(name, options) {
- const query = {
- name,
- };
-
- return this.findOne(query, options);
- }
-
- findByUpdatedDate(updatedAfterDate, options) {
- const query = {
- _updatedAt: { $gte: new Date(updatedAfterDate) },
- };
-
- return this.find(query, options);
- }
-
- canAddUserToRole(uid, roleName, scope) {
- const role = this.findOne({ name: roleName }, { fields: { scope: 1 } });
- if (!role) {
- return false;
- }
-
- const model = Models[role.scope];
- if (!model) {
- return;
- }
-
- const user = model.isUserInRoleScope(uid, scope);
- return !!user;
- }
-}
-
-export default new Roles('roles');
diff --git a/app/models/server/models/Rooms.js b/app/models/server/models/Rooms.js
index d884208317fc..86076aaa4368 100644
--- a/app/models/server/models/Rooms.js
+++ b/app/models/server/models/Rooms.js
@@ -5,7 +5,6 @@ import { escapeRegExp } from '@rocket.chat/string-helpers';
import { Base } from './_Base';
import Messages from './Messages';
import Subscriptions from './Subscriptions';
-import { getValidRoomName } from '../../../utils';
export class Rooms extends Base {
constructor(...args) {
@@ -59,6 +58,35 @@ export class Rooms extends Base {
return this.update(query, update);
}
+ setCallStatus(_id, status) {
+ const query = {
+ _id,
+ };
+
+ const update = {
+ $set: {
+ callStatus: status,
+ },
+ };
+
+ return this.update(query, update);
+ }
+
+ setCallStatusAndCallStartTime(_id, status) {
+ const query = {
+ _id,
+ };
+
+ const update = {
+ $set: {
+ callStatus: status,
+ webRtcCallStartTime: new Date(),
+ },
+ };
+
+ return this.update(query, update);
+ }
+
findByTokenpass(tokens) {
const query = {
'tokenpass.tokens.token': {
@@ -335,6 +363,7 @@ export class Rooms extends Base {
let channelName = s.trim(name);
try {
// TODO evaluate if this function call should be here
+ const { getValidRoomName } = import('../../../utils/lib/getValidRoomName');
channelName = getValidRoomName(channelName, null, { allowDuplicates: true });
} catch (e) {
console.error(e);
diff --git a/app/models/server/models/ServerEvents.ts b/app/models/server/models/ServerEvents.ts
deleted file mode 100644
index 09b17ac51067..000000000000
--- a/app/models/server/models/ServerEvents.ts
+++ /dev/null
@@ -1,11 +0,0 @@
-import { Base } from './_Base';
-
-export class ServerEvents extends Base {
- constructor() {
- super('server_events');
- this.tryEnsureIndex({ t: 1, ip: 1, ts: -1 });
- this.tryEnsureIndex({ t: 1, 'u.username': 1, ts: -1 });
- }
-}
-
-export default new ServerEvents();
diff --git a/app/models/server/models/Sessions.js b/app/models/server/models/Sessions.js
deleted file mode 100644
index ffb43d6566a5..000000000000
--- a/app/models/server/models/Sessions.js
+++ /dev/null
@@ -1,717 +0,0 @@
-import { Base } from './_Base';
-import { readSecondaryPreferred } from '../../../../server/database/readSecondaryPreferred';
-
-export const aggregates = {
- dailySessionsOfYesterday(collection, { year, month, day }) {
- return collection.aggregate([{
- $match: {
- userId: { $exists: true },
- lastActivityAt: { $exists: true },
- device: { $exists: true },
- type: 'session',
- $or: [{
- year: { $lt: year },
- }, {
- year,
- month: { $lt: month },
- }, {
- year,
- month,
- day: { $lte: day },
- }],
- },
- }, {
- $project: {
- userId: 1,
- device: 1,
- day: 1,
- month: 1,
- year: 1,
- mostImportantRole: 1,
- time: { $trunc: { $divide: [{ $subtract: ['$lastActivityAt', '$loginAt'] }, 1000] } },
- },
- }, {
- $match: {
- time: { $gt: 0 },
- },
- }, {
- $group: {
- _id: {
- userId: '$userId',
- device: '$device',
- day: '$day',
- month: '$month',
- year: '$year',
- },
- mostImportantRole: { $first: '$mostImportantRole' },
- time: { $sum: '$time' },
- sessions: { $sum: 1 },
- },
- }, {
- $sort: {
- time: -1,
- },
- }, {
- $group: {
- _id: {
- userId: '$_id.userId',
- day: '$_id.day',
- month: '$_id.month',
- year: '$_id.year',
- },
- mostImportantRole: { $first: '$mostImportantRole' },
- time: { $sum: '$time' },
- sessions: { $sum: '$sessions' },
- devices: {
- $push: {
- sessions: '$sessions',
- time: '$time',
- device: '$_id.device',
- },
- },
- },
- }, {
- $sort: {
- _id: 1,
- },
- }, {
- $project: {
- _id: 0,
- type: { $literal: 'user_daily' },
- _computedAt: { $literal: new Date() },
- day: '$_id.day',
- month: '$_id.month',
- year: '$_id.year',
- userId: '$_id.userId',
- mostImportantRole: 1,
- time: 1,
- sessions: 1,
- devices: 1,
- },
- }], { allowDiskUse: true });
- },
-
- getUniqueUsersOfYesterday(collection, { year, month, day }) {
- return collection.aggregate([{
- $match: {
- year,
- month,
- day,
- type: 'user_daily',
- },
- }, {
- $group: {
- _id: {
- day: '$day',
- month: '$month',
- year: '$year',
- mostImportantRole: '$mostImportantRole',
- },
- count: {
- $sum: 1,
- },
- sessions: {
- $sum: '$sessions',
- },
- time: {
- $sum: '$time',
- },
- },
- }, {
- $group: {
- _id: {
- day: '$day',
- month: '$month',
- year: '$year',
- },
- roles: {
- $push: {
- role: '$_id.mostImportantRole',
- count: '$count',
- sessions: '$sessions',
- time: '$time',
- },
- },
- count: {
- $sum: '$count',
- },
- sessions: {
- $sum: '$sessions',
- },
- time: {
- $sum: '$time',
- },
- },
- }, {
- $project: {
- _id: 0,
- count: 1,
- sessions: 1,
- time: 1,
- roles: 1,
- },
- }]).toArray();
- },
-
- getUniqueUsersOfLastMonthOrWeek(collection, { year, month, day, type = 'month' }) {
- return collection.aggregate([{
- $match: {
- type: 'user_daily',
- ...aggregates.getMatchOfLastMonthOrWeek({ year, month, day, type }),
- },
- }, {
- $group: {
- _id: {
- userId: '$userId',
- },
- mostImportantRole: { $first: '$mostImportantRole' },
- sessions: {
- $sum: '$sessions',
- },
- time: {
- $sum: '$time',
- },
- },
- }, {
- $group: {
- _id: {
- mostImportantRole: '$mostImportantRole',
- },
- count: {
- $sum: 1,
- },
- sessions: {
- $sum: '$sessions',
- },
- time: {
- $sum: '$time',
- },
- },
- }, {
- $sort: {
- time: -1,
- },
- }, {
- $group: {
- _id: 1,
- roles: {
- $push: {
- role: '$_id.mostImportantRole',
- count: '$count',
- sessions: '$sessions',
- time: '$time',
- },
- },
- count: {
- $sum: '$count',
- },
- sessions: {
- $sum: '$sessions',
- },
- time: {
- $sum: '$time',
- },
- },
- }, {
- $project: {
- _id: 0,
- count: 1,
- roles: 1,
- sessions: 1,
- time: 1,
- },
- }], { allowDiskUse: true }).toArray();
- },
-
- getMatchOfLastMonthOrWeek({ year, month, day, type = 'month' }) {
- let startOfPeriod;
-
- if (type === 'month') {
- const pastMonthLastDay = new Date(year, month - 1, 0).getDate();
- const currMonthLastDay = new Date(year, month, 0).getDate();
-
- startOfPeriod = new Date(year, month - 1, day);
- startOfPeriod.setMonth(startOfPeriod.getMonth() - 1, (currMonthLastDay === day ? pastMonthLastDay : Math.min(pastMonthLastDay, day)) + 1);
- } else {
- startOfPeriod = new Date(year, month - 1, day - 6);
- }
-
- const startOfPeriodObject = {
- year: startOfPeriod.getFullYear(),
- month: startOfPeriod.getMonth() + 1,
- day: startOfPeriod.getDate(),
- };
-
- if (year === startOfPeriodObject.year && month === startOfPeriodObject.month) {
- return {
- year,
- month,
- day: { $gte: startOfPeriodObject.day, $lte: day },
- };
- }
-
- if (year === startOfPeriodObject.year) {
- return {
- year,
- $and: [{
- $or: [{
- month: { $gt: startOfPeriodObject.month },
- }, {
- month: startOfPeriodObject.month,
- day: { $gte: startOfPeriodObject.day },
- }],
- }, {
- $or: [{
- month: { $lt: month },
- }, {
- month,
- day: { $lte: day },
- }],
- }],
- };
- }
-
- return {
- $and: [{
- $or: [{
- year: { $gt: startOfPeriodObject.year },
- }, {
- year: startOfPeriodObject.year,
- month: { $gt: startOfPeriodObject.month },
- }, {
- year: startOfPeriodObject.year,
- month: startOfPeriodObject.month,
- day: { $gte: startOfPeriodObject.day },
- }],
- }, {
- $or: [{
- year: { $lt: year },
- }, {
- year,
- month: { $lt: month },
- }, {
- year,
- month,
- day: { $lte: day },
- }],
- }],
- };
- },
-
- getUniqueDevicesOfLastMonthOrWeek(collection, { year, month, day, type = 'month' }) {
- return collection.aggregate([{
- $match: {
- type: 'user_daily',
- ...aggregates.getMatchOfLastMonthOrWeek({ year, month, day, type }),
- },
- }, {
- $unwind: '$devices',
- }, {
- $group: {
- _id: {
- type: '$devices.device.type',
- name: '$devices.device.name',
- version: '$devices.device.version',
- },
- count: {
- $sum: '$devices.sessions',
- },
- time: {
- $sum: '$devices.time',
- },
- },
- }, {
- $sort: {
- time: -1,
- },
- }, {
- $project: {
- _id: 0,
- type: '$_id.type',
- name: '$_id.name',
- version: '$_id.version',
- count: 1,
- time: 1,
- },
- }], { allowDiskUse: true }).toArray();
- },
-
- getUniqueDevicesOfYesterday(collection, { year, month, day }) {
- return collection.aggregate([{
- $match: {
- year,
- month,
- day,
- type: 'user_daily',
- },
- }, {
- $unwind: '$devices',
- }, {
- $group: {
- _id: {
- type: '$devices.device.type',
- name: '$devices.device.name',
- version: '$devices.device.version',
- },
- count: {
- $sum: '$devices.sessions',
- },
- time: {
- $sum: '$devices.time',
- },
- },
- }, {
- $sort: {
- time: -1,
- },
- }, {
- $project: {
- _id: 0,
- type: '$_id.type',
- name: '$_id.name',
- version: '$_id.version',
- count: 1,
- time: 1,
- },
- }]).toArray();
- },
-
- getUniqueOSOfLastMonthOrWeek(collection, { year, month, day, type = 'month' }) {
- return collection.aggregate([{
- $match: {
- type: 'user_daily',
- 'devices.device.os.name': {
- $exists: true,
- },
- ...aggregates.getMatchOfLastMonthOrWeek({ year, month, day, type }),
- },
- }, {
- $unwind: '$devices',
- }, {
- $group: {
- _id: {
- name: '$devices.device.os.name',
- version: '$devices.device.os.version',
- },
- count: {
- $sum: '$devices.sessions',
- },
- time: {
- $sum: '$devices.time',
- },
- },
- }, {
- $sort: {
- time: -1,
- },
- }, {
- $project: {
- _id: 0,
- name: '$_id.name',
- version: '$_id.version',
- count: 1,
- time: 1,
- },
- }], { allowDiskUse: true }).toArray();
- },
-
- getUniqueOSOfYesterday(collection, { year, month, day }) {
- return collection.aggregate([{
- $match: {
- year,
- month,
- day,
- type: 'user_daily',
- 'devices.device.os.name': {
- $exists: true,
- },
- },
- }, {
- $unwind: '$devices',
- }, {
- $group: {
- _id: {
- name: '$devices.device.os.name',
- version: '$devices.device.os.version',
- },
- count: {
- $sum: '$devices.sessions',
- },
- time: {
- $sum: '$devices.time',
- },
- },
- }, {
- $sort: {
- time: -1,
- },
- }, {
- $project: {
- _id: 0,
- name: '$_id.name',
- version: '$_id.version',
- count: 1,
- time: 1,
- },
- }]).toArray();
- },
-};
-
-export class Sessions extends Base {
- constructor(...args) {
- super(...args);
-
- this.tryEnsureIndex({ instanceId: 1, sessionId: 1, year: 1, month: 1, day: 1 });
- this.tryEnsureIndex({ instanceId: 1, sessionId: 1, userId: 1 });
- this.tryEnsureIndex({ instanceId: 1, sessionId: 1 });
- this.tryEnsureIndex({ sessionId: 1 });
- this.tryEnsureIndex({ userId: 1 });
- this.tryEnsureIndex({ year: 1, month: 1, day: 1, type: 1 });
- this.tryEnsureIndex({ type: 1 });
- this.tryEnsureIndex({ ip: 1, loginAt: 1 });
- this.tryEnsureIndex({ _computedAt: 1 }, { expireAfterSeconds: 60 * 60 * 24 * 45 });
-
- const db = this.model.rawDatabase();
- this.secondaryCollection = db.collection(this.model._name, { readPreference: readSecondaryPreferred(db) });
- }
-
- getUniqueUsersOfYesterday() {
- const date = new Date();
- date.setDate(date.getDate() - 1);
-
- const year = date.getFullYear();
- const month = date.getMonth() + 1;
- const day = date.getDate();
-
- return {
- year,
- month,
- day,
- data: Promise.await(aggregates.getUniqueUsersOfYesterday(this.secondaryCollection, { year, month, day })),
- };
- }
-
- getUniqueUsersOfLastMonth() {
- const date = new Date();
- date.setDate(date.getDate() - 1);
-
- const year = date.getFullYear();
- const month = date.getMonth() + 1;
- const day = date.getDate();
-
- return {
- year,
- month,
- day,
- data: Promise.await(aggregates.getUniqueUsersOfLastMonthOrWeek(this.secondaryCollection, { year, month, day })),
- };
- }
-
- getUniqueUsersOfLastWeek() {
- const date = new Date();
- date.setDate(date.getDate() - 1);
-
- const year = date.getFullYear();
- const month = date.getMonth() + 1;
- const day = date.getDate();
-
- return {
- year,
- month,
- day,
- data: Promise.await(aggregates.getUniqueUsersOfLastMonthOrWeek(this.secondaryCollection, { year, month, day, type: 'week' })),
- };
- }
-
- getUniqueDevicesOfYesterday() {
- const date = new Date();
- date.setDate(date.getDate() - 1);
-
- const year = date.getFullYear();
- const month = date.getMonth() + 1;
- const day = date.getDate();
-
- return {
- year,
- month,
- day,
- data: Promise.await(aggregates.getUniqueDevicesOfYesterday(this.secondaryCollection, { year, month, day })),
- };
- }
-
- getUniqueDevicesOfLastMonth() {
- const date = new Date();
- date.setDate(date.getDate() - 1);
-
- const year = date.getFullYear();
- const month = date.getMonth() + 1;
- const day = date.getDate();
-
- return {
- year,
- month,
- day,
- data: Promise.await(aggregates.getUniqueDevicesOfLastMonthOrWeek(this.secondaryCollection, { year, month, day })),
- };
- }
-
- getUniqueDevicesOfLastWeek() {
- const date = new Date();
- date.setDate(date.getDate() - 1);
-
- const year = date.getFullYear();
- const month = date.getMonth() + 1;
- const day = date.getDate();
-
- return {
- year,
- month,
- day,
- data: Promise.await(aggregates.getUniqueDevicesOfLastMonthOrWeek(this.secondaryCollection, { year, month, day, type: 'week' })),
- };
- }
-
- getUniqueOSOfYesterday() {
- const date = new Date();
- date.setDate(date.getDate() - 1);
-
- const year = date.getFullYear();
- const month = date.getMonth() + 1;
- const day = date.getDate();
-
- return {
- year,
- month,
- day,
- data: Promise.await(aggregates.getUniqueOSOfYesterday(this.secondaryCollection, { year, month, day })),
- };
- }
-
- getUniqueOSOfLastMonth() {
- const date = new Date();
- date.setDate(date.getDate() - 1);
-
- const year = date.getFullYear();
- const month = date.getMonth() + 1;
- const day = date.getDate();
-
- return {
- year,
- month,
- day,
- data: Promise.await(aggregates.getUniqueOSOfLastMonthOrWeek(this.secondaryCollection, { year, month, day })),
- };
- }
-
- getUniqueOSOfLastWeek() {
- const date = new Date();
- date.setDate(date.getDate() - 1);
-
- const year = date.getFullYear();
- const month = date.getMonth() + 1;
- const day = date.getDate();
-
- return {
- year,
- month,
- day,
- data: Promise.await(aggregates.getUniqueOSOfLastMonthOrWeek(this.secondaryCollection, { year, month, day, type: 'week' })),
- };
- }
-
- createOrUpdate(data = {}) {
- const { year, month, day, sessionId, instanceId } = data;
-
- if (!year || !month || !day || !sessionId || !instanceId) {
- return;
- }
-
- const now = new Date();
-
- return this.upsert({ instanceId, sessionId, year, month, day }, {
- $set: data,
- $setOnInsert: {
- createdAt: now,
- },
- });
- }
-
- closeByInstanceIdAndSessionId(instanceId, sessionId) {
- const query = {
- instanceId,
- sessionId,
- closedAt: { $exists: 0 },
- };
-
- const closeTime = new Date();
- const update = {
- $set: {
- closedAt: closeTime,
- lastActivityAt: closeTime,
- },
- };
-
- return this.update(query, update);
- }
-
- updateActiveSessionsByDateAndInstanceIdAndIds({ year, month, day } = {}, instanceId, sessions, data = {}) {
- const query = {
- instanceId,
- year,
- month,
- day,
- sessionId: { $in: sessions },
- closedAt: { $exists: 0 },
- };
-
- const update = {
- $set: data,
- };
-
- return this.update(query, update, { multi: true });
- }
-
- logoutByInstanceIdAndSessionIdAndUserId(instanceId, sessionId, userId) {
- const query = {
- instanceId,
- sessionId,
- userId,
- logoutAt: { $exists: 0 },
- };
-
- const logoutAt = new Date();
- const update = {
- $set: {
- logoutAt,
- },
- };
-
- return this.update(query, update, { multi: true });
- }
-
- createBatch(sessions) {
- if (!sessions || sessions.length === 0) {
- return;
- }
-
- const ops = [];
- sessions.forEach((doc) => {
- const { year, month, day, sessionId, instanceId } = doc;
- delete doc._id;
-
- ops.push({
- updateOne: {
- filter: { year, month, day, sessionId, instanceId },
- update: {
- $set: doc,
- },
- upsert: true,
- },
- });
- });
-
- return this.model.rawCollection().bulkWrite(ops, { ordered: false });
- }
-}
-
-export default new Sessions('sessions');
diff --git a/app/models/server/models/Sessions.mocks.js b/app/models/server/models/Sessions.mocks.js
deleted file mode 100644
index ac4b22bf7780..000000000000
--- a/app/models/server/models/Sessions.mocks.js
+++ /dev/null
@@ -1,16 +0,0 @@
-import mock from 'mock-require';
-
-mock('./_Base', {
- Base: class Base {
- model = {
- rawDatabase() {
- return {
- collection() {},
- options: {},
- };
- },
- }
-
- tryEnsureIndex() {}
- },
-});
diff --git a/app/models/server/models/SmarshHistory.js b/app/models/server/models/SmarshHistory.js
deleted file mode 100644
index 9b2b7abc5043..000000000000
--- a/app/models/server/models/SmarshHistory.js
+++ /dev/null
@@ -1,9 +0,0 @@
-import { Base } from './_Base';
-
-export class SmarshHistory extends Base {
- constructor() {
- super('smarsh_history');
- }
-}
-
-export default new SmarshHistory();
diff --git a/app/models/server/models/Statistics.js b/app/models/server/models/Statistics.js
deleted file mode 100644
index 014f8c8180d2..000000000000
--- a/app/models/server/models/Statistics.js
+++ /dev/null
@@ -1,28 +0,0 @@
-import { Base } from './_Base';
-
-export class Statistics extends Base {
- constructor() {
- super('statistics');
-
- this.tryEnsureIndex({ createdAt: -1 });
- }
-
- // FIND ONE
- findOneById(_id, options) {
- const query = { _id };
- return this.findOne(query, options);
- }
-
- findLast() {
- const options = {
- sort: {
- createdAt: -1,
- },
- limit: 1,
- };
- const records = this.find({}, options).fetch();
- return records && records[0];
- }
-}
-
-export default new Statistics();
diff --git a/app/models/server/models/Uploads.js b/app/models/server/models/Uploads.js
deleted file mode 100644
index ce56ea6d0c0f..000000000000
--- a/app/models/server/models/Uploads.js
+++ /dev/null
@@ -1,146 +0,0 @@
-import _ from 'underscore';
-import s from 'underscore.string';
-import { InstanceStatus } from 'meteor/konecty:multiple-instances-status';
-import { escapeRegExp } from '@rocket.chat/string-helpers';
-
-import { Base } from './_Base';
-
-const fillTypeGroup = (fileData) => {
- if (!fileData.type) {
- return;
- }
-
- fileData.typeGroup = fileData.type.split('/').shift();
-};
-
-export class Uploads extends Base {
- constructor() {
- super('uploads');
-
- this.model.before.insert((userId, doc) => {
- doc.instanceId = InstanceStatus.id();
- });
-
- this.tryEnsureIndex({ rid: 1 });
- this.tryEnsureIndex({ uploadedAt: 1 });
- this.tryEnsureIndex({ typeGroup: 1 });
- }
-
- findNotHiddenFilesOfRoom(roomId, searchText, fileType, limit) {
- const fileQuery = {
- rid: roomId,
- complete: true,
- uploading: false,
- _hidden: {
- $ne: true,
- },
- };
-
- if (searchText) {
- fileQuery.name = { $regex: new RegExp(escapeRegExp(searchText), 'i') };
- }
-
- if (fileType && fileType !== 'all') {
- fileQuery.typeGroup = fileType;
- }
-
- const fileOptions = {
- limit,
- sort: {
- uploadedAt: -1,
- },
- fields: {
- _id: 1,
- userId: 1,
- rid: 1,
- name: 1,
- description: 1,
- type: 1,
- url: 1,
- uploadedAt: 1,
- typeGroup: 1,
- },
- };
-
- return this.find(fileQuery, fileOptions);
- }
-
- insert(fileData, ...args) {
- fillTypeGroup(fileData);
- return super.insert(fileData, ...args);
- }
-
- update(filter, update, ...args) {
- if (update.$set) {
- fillTypeGroup(update.$set);
- } else if (update.type) {
- fillTypeGroup(update);
- }
-
- return super.update(filter, update, ...args);
- }
-
- insertFileInit(userId, store, file, extra) {
- const fileData = {
- userId,
- store,
- complete: false,
- uploading: true,
- progress: 0,
- extension: s.strRightBack(file.name, '.'),
- uploadedAt: new Date(),
- };
-
- _.extend(fileData, file, extra);
-
- if (this.model.direct && this.model.direct.insert != null) {
- fillTypeGroup(fileData);
- file = this.model.direct.insert(fileData);
- } else {
- file = this.insert(fileData);
- }
-
- return file;
- }
-
- updateFileComplete(fileId, userId, file) {
- let result;
- if (!fileId) {
- return;
- }
-
- const filter = {
- _id: fileId,
- userId,
- };
-
- const update = {
- $set: {
- complete: true,
- uploading: false,
- progress: 1,
- },
- };
-
- update.$set = _.extend(file, update.$set);
-
- if (this.model.direct && this.model.direct.update != null) {
- fillTypeGroup(update.$set);
-
- result = this.model.direct.update(filter, update);
- } else {
- result = this.update(filter, update);
- }
-
- return result;
- }
-
- deleteFile(fileId) {
- if (this.model.direct && this.model.direct.remove != null) {
- return this.model.direct.remove({ _id: fileId });
- }
- return this.remove({ _id: fileId });
- }
-}
-
-export default new Uploads();
diff --git a/app/models/server/models/UserDataFiles.js b/app/models/server/models/UserDataFiles.js
deleted file mode 100644
index a877188ff03b..000000000000
--- a/app/models/server/models/UserDataFiles.js
+++ /dev/null
@@ -1,44 +0,0 @@
-import _ from 'underscore';
-
-import { Base } from './_Base';
-
-export class UserDataFiles extends Base {
- constructor() {
- super('user_data_files');
-
- this.tryEnsureIndex({ userId: 1 });
- }
-
- // FIND
- findById(id) {
- const query = { _id: id };
- return this.find(query);
- }
-
- findLastFileByUser(userId, options = {}) {
- const query = {
- userId,
- };
-
- options.sort = { _updatedAt: -1 };
- return this.findOne(query, options);
- }
-
- // INSERT
- create(data) {
- const userDataFile = {
- createdAt: new Date(),
- };
-
- _.extend(userDataFile, data);
-
- return this.insert(userDataFile);
- }
-
- // REMOVE
- removeById(_id) {
- return this.remove(_id);
- }
-}
-
-export default new UserDataFiles();
diff --git a/app/models/server/models/Users.js b/app/models/server/models/Users.js
index 6b755bd01a02..f33198de0abe 100644
--- a/app/models/server/models/Users.js
+++ b/app/models/server/models/Users.js
@@ -1436,23 +1436,6 @@ export class Users extends Base {
return this.find(query).count() !== 0;
}
- addBannerById(_id, banner) {
- const query = {
- _id,
- [`banners.${ banner.id }.read`]: {
- $ne: true,
- },
- };
-
- const update = {
- $set: {
- [`banners.${ banner.id }`]: banner,
- },
- };
-
- return this.update(query, update);
- }
-
setBannerReadById(_id, bannerId) {
const update = {
$set: {
diff --git a/app/models/server/models/UsersSessions.js b/app/models/server/models/UsersSessions.js
deleted file mode 100644
index 43aec902d343..000000000000
--- a/app/models/server/models/UsersSessions.js
+++ /dev/null
@@ -1,7 +0,0 @@
-import { UsersSessions } from 'meteor/konecty:user-presence';
-
-import { Base } from './_Base';
-
-export class UsersSessionsModel extends Base {}
-
-export default new UsersSessionsModel(UsersSessions, { preventSetUpdatedAt: true });
diff --git a/app/models/server/models/WebdavAccounts.js b/app/models/server/models/WebdavAccounts.js
deleted file mode 100644
index 09df0b64a3a6..000000000000
--- a/app/models/server/models/WebdavAccounts.js
+++ /dev/null
@@ -1,27 +0,0 @@
-/**
- * Webdav Accounts model
- */
-import { Base } from './_Base';
-
-export class WebdavAccounts extends Base {
- constructor() {
- super('webdav_accounts');
-
- this.tryEnsureIndex({ user_id: 1 });
- }
-
- findWithUserId(user_id, options) {
- const query = { user_id };
- return this.find(query, options);
- }
-
- removeByUserAndId(_id, user_id) {
- return this.remove({ _id, user_id });
- }
-
- removeById(_id) {
- return this.remove({ _id });
- }
-}
-
-export default new WebdavAccounts();
diff --git a/app/models/server/models/_BaseDb.js b/app/models/server/models/_BaseDb.js
index 6c9473c81fb8..f72bb6c84557 100644
--- a/app/models/server/models/_BaseDb.js
+++ b/app/models/server/models/_BaseDb.js
@@ -30,31 +30,14 @@ const actions = {
d: 'remove',
};
-export class BaseDb extends EventEmitter {
- constructor(model, baseModel, options = {}) {
+export class BaseDbWatch extends EventEmitter {
+ constructor(collectionName) {
super();
-
- if (Match.test(model, String)) {
- this.name = model;
- this.collectionName = this.baseName + this.name;
- this.model = new Mongo.Collection(this.collectionName);
- } else {
- this.name = model._name;
- this.collectionName = this.name;
- this.model = model;
- }
-
- this.baseModel = baseModel;
-
- this.preventSetUpdatedAt = !!options.preventSetUpdatedAt;
-
- this.wrapModel();
+ this.collectionName = collectionName;
if (!process.env.DISABLE_DB_WATCH) {
this.initDbWatch();
}
-
- this.tryEnsureIndex({ _updatedAt: 1 }, options._updatedAtIndexOptions);
}
initDbWatch() {
@@ -97,6 +80,104 @@ export class BaseDb extends EventEmitter {
}
}
+ processOplogRecord({ id, op }) {
+ const action = actions[op.op];
+ metrics.oplog.inc({
+ collection: this.collectionName,
+ op: action,
+ });
+
+ if (action === 'insert') {
+ this.emit('change', {
+ action,
+ clientAction: 'inserted',
+ id: op.o._id,
+ data: op.o,
+ oplog: true,
+ });
+ return;
+ }
+
+ if (action === 'update') {
+ if (!op.o.$set && !op.o.$unset) {
+ this.emit('change', {
+ action,
+ clientAction: 'updated',
+ id,
+ data: op.o,
+ oplog: true,
+ });
+ return;
+ }
+
+ const diff = {};
+ if (op.o.$set) {
+ for (const key in op.o.$set) {
+ if (op.o.$set.hasOwnProperty(key)) {
+ diff[key] = op.o.$set[key];
+ }
+ }
+ }
+ const unset = {};
+ if (op.o.$unset) {
+ for (const key in op.o.$unset) {
+ if (op.o.$unset.hasOwnProperty(key)) {
+ diff[key] = undefined;
+ unset[key] = 1;
+ }
+ }
+ }
+
+ this.emit('change', {
+ action,
+ clientAction: 'updated',
+ id,
+ diff,
+ unset,
+ oplog: true,
+ });
+ return;
+ }
+
+ if (action === 'remove') {
+ this.emit('change', {
+ action,
+ clientAction: 'removed',
+ id,
+ oplog: true,
+ });
+ }
+ }
+}
+
+
+export class BaseDb extends BaseDbWatch {
+ constructor(model, baseModel, options = {}) {
+ const collectionName = Match.test(model, String) ? baseName + model : model._name;
+
+ super(collectionName);
+
+ this.collectionName = collectionName;
+
+ if (Match.test(model, String)) {
+ this.name = model;
+ this.collectionName = this.baseName + this.name;
+ this.model = new Mongo.Collection(this.collectionName);
+ } else {
+ this.name = model._name;
+ this.collectionName = this.name;
+ this.model = model;
+ }
+
+ this.baseModel = baseModel;
+
+ this.preventSetUpdatedAt = !!options.preventSetUpdatedAt;
+
+ this.wrapModel();
+
+ this.tryEnsureIndex({ _updatedAt: 1 }, options._updatedAtIndexOptions);
+ }
+
get baseName() {
return baseName;
}
@@ -204,75 +285,6 @@ export class BaseDb extends EventEmitter {
);
}
- processOplogRecord({ id, op }) {
- const action = actions[op.op];
- metrics.oplog.inc({
- collection: this.collectionName,
- op: action,
- });
-
- if (action === 'insert') {
- this.emit('change', {
- action,
- clientAction: 'inserted',
- id: op.o._id,
- data: op.o,
- oplog: true,
- });
- return;
- }
-
- if (action === 'update') {
- if (!op.o.$set && !op.o.$unset) {
- this.emit('change', {
- action,
- clientAction: 'updated',
- id,
- data: op.o,
- oplog: true,
- });
- return;
- }
-
- const diff = {};
- if (op.o.$set) {
- for (const key in op.o.$set) {
- if (op.o.$set.hasOwnProperty(key)) {
- diff[key] = op.o.$set[key];
- }
- }
- }
- const unset = {};
- if (op.o.$unset) {
- for (const key in op.o.$unset) {
- if (op.o.$unset.hasOwnProperty(key)) {
- diff[key] = undefined;
- unset[key] = 1;
- }
- }
- }
-
- this.emit('change', {
- action,
- clientAction: 'updated',
- id,
- diff,
- unset,
- oplog: true,
- });
- return;
- }
-
- if (action === 'remove') {
- this.emit('change', {
- action,
- clientAction: 'removed',
- id,
- oplog: true,
- });
- }
- }
-
insert(record, ...args) {
this.setUpdatedAt(record);
diff --git a/app/models/server/models/_oplogHandle.ts b/app/models/server/models/_oplogHandle.ts
index 997da3c34eb7..936881eeecaa 100644
--- a/app/models/server/models/_oplogHandle.ts
+++ b/app/models/server/models/_oplogHandle.ts
@@ -1,5 +1,4 @@
import { Meteor } from 'meteor/meteor';
-import { Promise } from 'meteor/promise';
import { MongoInternals, OplogHandle } from 'meteor/mongo';
import semver from 'semver';
import { MongoClient, Cursor, Timestamp, Db } from 'mongodb';
@@ -193,12 +192,12 @@ class CustomOplogHandle {
}
}
-let oplogHandle: Promise;
+let oplogHandle: CustomOplogHandle;
if (!process.env.DISABLE_DB_WATCH) {
- // @ts-ignore
- // eslint-disable-next-line no-undef
- if (Package['disable-oplog']) {
+ const disableOplog = !!(global.Package as any)['disable-oplog'];
+
+ if (disableOplog) {
try {
oplogHandle = Promise.await(new CustomOplogHandle().start());
} catch (e) {
diff --git a/app/models/server/models/apps-persistence-model.js b/app/models/server/models/apps-persistence-model.js
index da178a390c32..dd01197abbc9 100644
--- a/app/models/server/models/apps-persistence-model.js
+++ b/app/models/server/models/apps-persistence-model.js
@@ -4,7 +4,7 @@ export class AppsPersistenceModel extends Base {
constructor() {
super('apps_persistence');
- this.tryEnsureIndex({ appId: 1 });
+ this.tryEnsureIndex({ appId: 1, associations: 1 });
}
// Bypass trash collection
diff --git a/app/models/server/raw/Analytics.js b/app/models/server/raw/Analytics.js
deleted file mode 100644
index 81e0ad335c26..000000000000
--- a/app/models/server/raw/Analytics.js
+++ /dev/null
@@ -1,151 +0,0 @@
-import { Random } from 'meteor/random';
-
-import { BaseRaw } from './BaseRaw';
-import Analytics from '../models/Analytics';
-import { readSecondaryPreferred } from '../../../../server/database/readSecondaryPreferred';
-
-export class AnalyticsRaw extends BaseRaw {
- saveMessageSent({ room, date }) {
- return this.update({ date, 'room._id': room._id, type: 'messages' }, {
- $set: {
- room: { _id: room._id, name: room.fname || room.name, t: room.t, usernames: room.usernames || [] },
- },
- $setOnInsert: {
- _id: Random.id(),
- date,
- type: 'messages',
- },
- $inc: { messages: 1 },
- }, { upsert: true });
- }
-
- saveUserData({ date }) {
- return this.update({ date, type: 'users' }, {
- $setOnInsert: {
- _id: Random.id(),
- date,
- type: 'users',
- },
- $inc: { users: 1 },
- }, { upsert: true });
- }
-
- saveMessageDeleted({ room, date }) {
- return this.update({ date, 'room._id': room._id }, {
- $inc: { messages: -1 },
- });
- }
-
- getMessagesSentTotalByDate({ start, end, options = {} }) {
- const params = [
- {
- $match: {
- type: 'messages',
- date: { $gte: start, $lte: end },
- },
- },
- {
- $group: {
- _id: '$date',
- messages: { $sum: '$messages' },
- },
- },
- ];
- if (options.sort) {
- params.push({ $sort: options.sort });
- }
- if (options.count) {
- params.push({ $limit: options.count });
- }
- return this.col.aggregate(params).toArray();
- }
-
- getMessagesOrigin({ start, end }) {
- const params = [
- {
- $match: {
- type: 'messages',
- date: { $gte: start, $lte: end },
- },
- },
- {
- $group: {
- _id: { t: '$room.t' },
- messages: { $sum: '$messages' },
- },
- },
- {
- $project: {
- _id: 0,
- t: '$_id.t',
- messages: 1,
- },
- },
- ];
- return this.col.aggregate(params).toArray();
- }
-
- getMostPopularChannelsByMessagesSentQuantity({ start, end, options = {} }) {
- const params = [
- {
- $match: {
- type: 'messages',
- date: { $gte: start, $lte: end },
- },
- },
- {
- $group: {
- _id: { t: '$room.t', name: '$room.name', usernames: '$room.usernames' },
- messages: { $sum: '$messages' },
- },
- },
- {
- $project: {
- _id: 0,
- t: '$_id.t',
- name: '$_id.name',
- usernames: '$_id.usernames',
- messages: 1,
- },
- },
- ];
- if (options.sort) {
- params.push({ $sort: options.sort });
- }
- if (options.count) {
- params.push({ $limit: options.count });
- }
- return this.col.aggregate(params).toArray();
- }
-
- getTotalOfRegisteredUsersByDate({ start, end, options = {} }) {
- const params = [
- {
- $match: {
- type: 'users',
- date: { $gte: start, $lte: end },
- },
- },
- {
- $group: {
- _id: '$date',
- users: { $sum: '$users' },
- },
- },
- ];
- if (options.sort) {
- params.push({ $sort: options.sort });
- }
- if (options.count) {
- params.push({ $limit: options.count });
- }
- return this.col.aggregate(params).toArray();
- }
-
- findByTypeBeforeDate({ type, date }) {
- return this.find({ type, date: { $lte: date } });
- }
-}
-
-const db = Analytics.model.rawDatabase();
-export default new AnalyticsRaw(db.collection(Analytics.model._name, { readPreference: readSecondaryPreferred(db) }));
diff --git a/app/models/server/raw/Analytics.ts b/app/models/server/raw/Analytics.ts
new file mode 100644
index 000000000000..4c2b1fa46cc6
--- /dev/null
+++ b/app/models/server/raw/Analytics.ts
@@ -0,0 +1,166 @@
+import { Random } from 'meteor/random';
+import { AggregationCursor, Cursor, SortOptionObject, UpdateWriteOpResult } from 'mongodb';
+
+import { BaseRaw, IndexSpecification } from './BaseRaw';
+import { IAnalytic } from '../../../../definition/IAnalytic';
+import { IRoom } from '../../../../definition/IRoom';
+
+type T = IAnalytic;
+
+export class AnalyticsRaw extends BaseRaw {
+ protected indexes: IndexSpecification[] = [
+ { key: { date: 1 } },
+ { key: { 'room._id': 1, date: 1 }, unique: true },
+ ];
+
+ saveMessageSent({ room, date }: { room: IRoom; date: IAnalytic['date'] }): Promise {
+ return this.updateMany({ date, 'room._id': room._id, type: 'messages' }, {
+ $set: {
+ room: {
+ _id: room._id,
+ name: room.fname || room.name,
+ t: room.t,
+ usernames: room.usernames || [],
+ },
+ },
+ $setOnInsert: {
+ _id: Random.id(),
+ date,
+ type: 'messages',
+ },
+ $inc: { messages: 1 },
+ }, { upsert: true });
+ }
+
+ saveUserData({ date }: { date: IAnalytic['date'] }): Promise {
+ return this.updateMany({ date, type: 'users' }, {
+ $setOnInsert: {
+ _id: Random.id(),
+ date,
+ type: 'users',
+ },
+ $inc: { users: 1 },
+ }, { upsert: true });
+ }
+
+ saveMessageDeleted({ room, date }: { room: { _id: string }; date: IAnalytic['date'] }): Promise {
+ return this.updateMany({ date, 'room._id': room._id }, {
+ $inc: { messages: -1 },
+ });
+ }
+
+ getMessagesSentTotalByDate({ start, end, options = {} }: { start: IAnalytic['date']; end: IAnalytic['date']; options?: { sort?: SortOptionObject; count?: number } }): AggregationCursor<{
+ _id: IAnalytic['date'];
+ messages: number;
+ }> {
+ return this.col.aggregate<{
+ _id: IAnalytic['date'];
+ messages: number;
+ }>([
+ {
+ $match: {
+ type: 'messages',
+ date: { $gte: start, $lte: end },
+ },
+ },
+ {
+ $group: {
+ _id: '$date',
+ messages: { $sum: '$messages' },
+ },
+ },
+ ...options.sort ? [{ $sort: options.sort }] : [],
+ ...options.count ? [{ $limit: options.count }] : [],
+ ]);
+ }
+
+ getMessagesOrigin({ start, end }: { start: IAnalytic['date']; end: IAnalytic['date'] }): AggregationCursor<{
+ t: IRoom['t'];
+ messages: number;
+ }> {
+ const params = [
+ {
+ $match: {
+ type: 'messages',
+ date: { $gte: start, $lte: end },
+ },
+ },
+ {
+ $group: {
+ _id: { t: '$room.t' },
+ messages: { $sum: '$messages' },
+ },
+ },
+ {
+ $project: {
+ _id: 0,
+ t: '$_id.t',
+ messages: 1,
+ },
+ },
+ ];
+ return this.col.aggregate(params);
+ }
+
+ getMostPopularChannelsByMessagesSentQuantity({ start, end, options = {} }: { start: IAnalytic['date']; end: IAnalytic['date']; options?: { sort?: SortOptionObject; count?: number } }): AggregationCursor<{
+ t: IRoom['t'];
+ name: string;
+ messages: number;
+ usernames: string[];
+ }> {
+ return this.col.aggregate([
+ {
+ $match: {
+ type: 'messages',
+ date: { $gte: start, $lte: end },
+ },
+ },
+ {
+ $group: {
+ _id: { t: '$room.t', name: '$room.name', usernames: '$room.usernames' },
+ messages: { $sum: '$messages' },
+ },
+ },
+ {
+ $project: {
+ _id: 0,
+ t: '$_id.t',
+ name: '$_id.name',
+ usernames: '$_id.usernames',
+ messages: 1,
+ },
+ },
+ ...options.sort ? [{ $sort: options.sort }] : [],
+ ...options.count ? [{ $limit: options.count }] : [],
+ ]);
+ }
+
+ getTotalOfRegisteredUsersByDate({ start, end, options = {} }: { start: IAnalytic['date']; end: IAnalytic['date']; options?: { sort?: SortOptionObject; count?: number } }): AggregationCursor<{
+ _id: IAnalytic['date'];
+ users: number;
+ }> {
+ return this.col.aggregate<{
+ _id: IAnalytic['date'];
+ users: number;
+ }>([
+ {
+ $match: {
+ type: 'users',
+ date: { $gte: start, $lte: end },
+ },
+ },
+ {
+ $group: {
+ _id: '$date',
+ users: { $sum: '$users' },
+ },
+ },
+ ...options.sort ? [{ $sort: options.sort }] : [],
+ ...options.count ? [{ $limit: options.count }] : [],
+ ]);
+ }
+
+ findByTypeBeforeDate({ type, date }: { type: T['type']; date: T['date'] }): Cursor {
+ return this.find({ type, date: { $lte: date } });
+ }
+}
diff --git a/app/models/server/raw/Avatars.ts b/app/models/server/raw/Avatars.ts
new file mode 100644
index 000000000000..cc9c5939f3b3
--- /dev/null
+++ b/app/models/server/raw/Avatars.ts
@@ -0,0 +1,73 @@
+import { DeleteWriteOpResultObject, UpdateWriteOpResult } from 'mongodb';
+
+import { BaseRaw, IndexSpecification } from './BaseRaw';
+import { IAvatar as T } from '../../../../definition/IAvatar';
+
+export class AvatarsRaw extends BaseRaw {
+ protected indexes: IndexSpecification[] = [
+ { key: { name: 1 }, sparse: true },
+ { key: { rid: 1 }, sparse: true },
+ ];
+
+ insertAvatarFileInit(name: string, userId: string, store: string, file: {name: string}, extra: object): Promise {
+ const fileData = {
+ name,
+ userId,
+ store,
+ complete: false,
+ uploading: true,
+ progress: 0,
+ extension: file.name.split('.').pop(),
+ uploadedAt: new Date(),
+ };
+
+ Object.assign(fileData, file, extra);
+
+ return this.updateOne({ _id: name }, fileData, { upsert: true });
+ }
+
+ updateFileComplete(fileId: string, userId: string, file: object): Promise | undefined {
+ if (!fileId) {
+ return;
+ }
+
+ const filter = {
+ _id: fileId,
+ userId,
+ };
+
+ const update = {
+ $set: {
+ complete: true,
+ uploading: false,
+ progress: 1,
+ },
+ };
+
+ update.$set = Object.assign(file, update.$set);
+
+ return this.updateOne(filter, update);
+ }
+
+ async findOneByName(name: string): Promise {
+ return this.findOne({ name });
+ }
+
+ async findOneByRoomId(rid: string): Promise {
+ return this.findOne({ rid });
+ }
+
+ async updateFileNameById(fileId: string, name: string): Promise {
+ const filter = { _id: fileId };
+ const update = {
+ $set: {
+ name,
+ },
+ };
+ return this.updateOne(filter, update);
+ }
+
+ async deleteFile(fileId: string): Promise {
+ return this.deleteOne({ _id: fileId });
+ }
+}
diff --git a/app/models/server/raw/Banners.ts b/app/models/server/raw/Banners.ts
index 301ea579bc00..a414332e2fa9 100644
--- a/app/models/server/raw/Banners.ts
+++ b/app/models/server/raw/Banners.ts
@@ -7,7 +7,7 @@ type T = IBanner;
export class BannersRaw extends BaseRaw {
constructor(
public readonly col: Collection,
- public readonly trash?: Collection,
+ trash?: Collection,
) {
super(col, trash);
diff --git a/app/models/server/raw/BannersDismiss.ts b/app/models/server/raw/BannersDismiss.ts
index bea87b687611..6c4e69f20b96 100644
--- a/app/models/server/raw/BannersDismiss.ts
+++ b/app/models/server/raw/BannersDismiss.ts
@@ -6,7 +6,7 @@ import { BaseRaw } from './BaseRaw';
export class BannersDismissRaw extends BaseRaw {
constructor(
public readonly col: Collection,
- public readonly trash?: Collection,
+ trash?: Collection,
) {
super(col, trash);
diff --git a/app/models/server/raw/BaseRaw.ts b/app/models/server/raw/BaseRaw.ts
index 602d8a62542a..47fd8bbdbdd1 100644
--- a/app/models/server/raw/BaseRaw.ts
+++ b/app/models/server/raw/BaseRaw.ts
@@ -1,10 +1,14 @@
import {
Collection,
CollectionInsertOneOptions,
+ CommonOptions,
Cursor,
DeleteWriteOpResultObject,
FilterQuery,
+ FindAndModifyWriteOpResultObject,
+ FindOneAndUpdateOption,
FindOneOptions,
+ IndexSpecification,
InsertOneWriteOpResult,
InsertWriteOpResult,
ObjectID,
@@ -19,8 +23,14 @@ import {
WriteOpResult,
} from 'mongodb';
+import {
+ IRocketChatRecord,
+ RocketChatRecordDeleted,
+} from '../../../../definition/IRocketChatRecord';
import { setUpdatedAt } from '../lib/setUpdatedAt';
+export { IndexSpecification } from 'mongodb';
+
// [extracted from @types/mongo] TypeScript Omit (Exclude to be specific) does not work for objects with an "any" indexed type, and breaks discriminated unions
type EnhancedOmit = string | number extends keyof T
? T // T has indexed type e.g. { _id: string; [k: string]: any; } or it is "any"
@@ -37,120 +47,206 @@ type ExtractIdType = TSchema extends { _id: infer U } // user has defin
: U
: ObjectId;
-type ModelOptionalId = EnhancedOmit & { _id?: ExtractIdType };
+export type ModelOptionalId = EnhancedOmit & { _id?: ExtractIdType };
// InsertionModel forces both _id and _updatedAt to be optional, regardless of how they are declared in T
-export type InsertionModel = EnhancedOmit, '_updatedAt'> & { _updatedAt?: Date };
+export type InsertionModel = EnhancedOmit, '_updatedAt'> & {
+ _updatedAt?: Date;
+};
export interface IBaseRaw {
col: Collection;
}
const baseName = 'rocketchat_';
-const isWithoutProjection = (props: T): props is WithoutProjection => !('projection' in props) && !('fields' in props);
type DefaultFields = Record | Record | void;
-type ResultFields = Defaults extends void ? Base : Defaults[keyof Defaults] extends 1 ? Pick : Omit;
+type ResultFields = Defaults extends void
+ ? Base
+ : Defaults[keyof Defaults] extends 1
+ ? Pick
+ : Omit;
const warnFields = process.env.NODE_ENV !== 'production'
- ? (...rest: any): void => { console.warn(...rest, new Error().stack); }
+ ? (...rest: any): void => {
+ console.warn(...rest, new Error().stack);
+ }
: new Function();
export class BaseRaw = undefined> implements IBaseRaw {
public readonly defaultFields: C;
+ protected indexes?: IndexSpecification[];
+
protected name: string;
+ private preventSetUpdatedAt: boolean;
+
+ public readonly trash?: Collection>;
+
constructor(
public readonly col: Collection,
- public readonly trash?: Collection,
+ trash?: Collection,
+ options?: { preventSetUpdatedAt?: boolean },
) {
this.name = this.col.collectionName.replace(baseName, '');
+ this.trash = trash as unknown as Collection>;
+
+ if (this.indexes?.length) {
+ this.col.createIndexes(this.indexes);
+ }
+
+ this.preventSetUpdatedAt = options?.preventSetUpdatedAt ?? false;
+ }
+
+ private doNotMixInclusionAndExclusionFields(options: FindOneOptions = {}): FindOneOptions {
+ const optionsDef = this.ensureDefaultFields(options);
+ if (optionsDef?.projection === undefined) {
+ return optionsDef;
+ }
+
+ const projection: Record = optionsDef?.projection;
+ const keys = Object.keys(projection);
+ const removeKeys = keys.filter((key) => projection[key] === 0);
+ if (keys.length > removeKeys.length) {
+ removeKeys.forEach((key) => delete projection[key]);
+ }
+
+ return {
+ ...optionsDef,
+ projection,
+ };
}
- private ensureDefaultFields(options?: undefined): C extends void ? undefined : WithoutProjection>;
+ private ensureDefaultFields(
+ options?: undefined,
+ ): C extends void ? undefined : WithoutProjection>;
- private ensureDefaultFields(options: WithoutProjection>): WithoutProjection>;
+ private ensureDefaultFields(
+ options: WithoutProjection>,
+ ): WithoutProjection>;
private ensureDefaultFields(options: FindOneOptions
): FindOneOptions
;
- private ensureDefaultFields
(options?: any): FindOneOptions
| undefined | WithoutProjection> {
+ private ensureDefaultFields(
+ options?: any,
+ ): FindOneOptions
| undefined | WithoutProjection> {
if (this.defaultFields === undefined) {
return options;
}
- const { fields, ...rest } = options || {};
+ const { fields: deprecatedFields, projection, ...rest } = options || {};
- if (fields) {
- warnFields('Using \'fields\' in models is deprecated.', options);
+ if (deprecatedFields) {
+ warnFields("Using 'fields' in models is deprecated.", options);
}
+ const fields = { ...deprecatedFields, ...projection };
+
return {
projection: this.defaultFields,
- ...fields && { projection: fields },
+ ...fields && Object.values(fields).length && { projection: fields },
...rest,
};
}
- async findOneById(_id: string, options?: WithoutProjection> | undefined): Promise;
+ public findOneAndUpdate(
+ query: FilterQuery,
+ update: UpdateQuery | T,
+ options?: FindOneAndUpdateOption,
+ ): Promise> {
+ return this.col.findOneAndUpdate(query, update, options);
+ }
+
+ async findOneById(
+ _id: string,
+ options?: WithoutProjection> | undefined,
+ ): Promise;
- async findOneById(_id: string, options: FindOneOptions
): Promise
;
+ async findOneById
(
+ _id: string,
+ options: FindOneOptions
,
+ ): Promise
;
async findOneById
(_id: string, options?: any): Promise {
const query = { _id } as FilterQuery;
- const optionsDef = this.ensureDefaultFields(options);
+ const optionsDef = this.doNotMixInclusionAndExclusionFields(options);
return this.col.findOne(query, optionsDef);
}
async findOne(query?: FilterQuery | string, options?: undefined): Promise;
- async findOne(query: FilterQuery | string, options: WithoutProjection>): Promise;
+ async findOne(
+ query: FilterQuery | string,
+ options: WithoutProjection>,
+ ): Promise;
- async findOne(query: FilterQuery | string, options: FindOneOptions): Promise
;
+ async findOne
(
+ query: FilterQuery | string,
+ options: FindOneOptions,
+ ): Promise
;
async findOne
(query: FilterQuery | string = {}, options?: any): Promise {
- const q = typeof query === 'string' ? { _id: query } as FilterQuery : query;
+ const q = typeof query === 'string' ? ({ _id: query } as FilterQuery) : query;
- const optionsDef = this.ensureDefaultFields(options);
+ const optionsDef = this.doNotMixInclusionAndExclusionFields(options);
return this.col.findOne(q, optionsDef);
}
- findUsersInRoles(): void {
- throw new Error('[overwrite-function] You must overwrite this function in the extended classes');
- }
+ // findUsersInRoles(): void {
+ // throw new Error('[overwrite-function] You must overwrite this function in the extended classes');
+ // }
find(query?: FilterQuery): Cursor>;
- find(query: FilterQuery, options: WithoutProjection>): Cursor>;
+ find(
+ query: FilterQuery,
+ options: WithoutProjection>,
+ ): Cursor>;
- find(query: FilterQuery, options: FindOneOptions): Cursor
;
+ find
(query: FilterQuery, options: FindOneOptions): Cursor
;
find
(query: FilterQuery | undefined = {}, options?: any): Cursor | Cursor {
- const optionsDef = this.ensureDefaultFields(options);
+ const optionsDef = this.doNotMixInclusionAndExclusionFields(options);
return this.col.find(query, optionsDef);
}
- update(filter: FilterQuery, update: UpdateQuery | Partial, options?: UpdateOneOptions & { multi?: boolean }): Promise {
- setUpdatedAt(update);
+ update(
+ filter: FilterQuery,
+ update: UpdateQuery | Partial,
+ options?: UpdateOneOptions & { multi?: boolean },
+ ): Promise {
+ this.setUpdatedAt(update);
return this.col.update(filter, update, options);
}
- updateOne(filter: FilterQuery, update: UpdateQuery | Partial, options?: UpdateOneOptions & { multi?: boolean }): Promise {
- setUpdatedAt(update);
+ updateOne(
+ filter: FilterQuery,
+ update: UpdateQuery | Partial,
+ options?: UpdateOneOptions & { multi?: boolean },
+ ): Promise {
+ this.setUpdatedAt(update);
return this.col.updateOne(filter, update, options);
}
- updateMany(filter: FilterQuery, update: UpdateQuery | Partial, options?: UpdateManyOptions): Promise {
- setUpdatedAt(update);
+ updateMany(
+ filter: FilterQuery,
+ update: UpdateQuery | Partial,
+ options?: UpdateManyOptions,
+ ): Promise {
+ this.setUpdatedAt(update);
return this.col.updateMany(filter, update, options);
}
- insertMany(docs: Array>, options?: CollectionInsertOneOptions): Promise>> {
+ insertMany(
+ docs: Array>,
+ options?: CollectionInsertOneOptions,
+ ): Promise>> {
docs = docs.map((doc) => {
if (!doc._id || typeof doc._id !== 'string') {
const oid = new ObjectID();
return { _id: oid.toHexString(), ...doc };
}
- setUpdatedAt(doc);
+ this.setUpdatedAt(doc);
return doc;
});
@@ -158,60 +254,187 @@ export class BaseRaw = undefined> implements IBase
return this.col.insertMany(docs as unknown as Array>, options);
}
- insertOne(doc: InsertionModel, options?: CollectionInsertOneOptions): Promise>> {
+ insertOne(
+ doc: InsertionModel,
+ options?: CollectionInsertOneOptions,
+ ): Promise>> {
if (!doc._id || typeof doc._id !== 'string') {
const oid = new ObjectID();
doc = { _id: oid.toHexString(), ...doc };
}
- setUpdatedAt(doc);
+ this.setUpdatedAt(doc);
// TODO reavaluate following type casting
return this.col.insertOne(doc as unknown as OptionalId, options);
}
removeById(_id: string): Promise {
- const query: object = { _id };
- return this.col.deleteOne(query);
+ return this.deleteOne({ _id } as FilterQuery);
}
- // Trash
- trashFind