>;
-export function getUsersInRole(
+export function getUsersInRole(
roleId: IRole['_id'],
scope: string | undefined,
- options: WithoutProjection>,
-): Promise>;
+ options: FindOptions,
+): Promise>;
export function getUsersInRole(
roleId: IRole['_id'],
scope: string | undefined,
- options: FindOneOptions
,
-): Promise>;
+ options?: any | undefined,
+): Promise> {
+ // TODO move the code from Roles.findUsersInRole to here and change all places to use this function
+ return Roles.findUsersInRole(roleId, scope, options);
+}
-export function getUsersInRole(
+export async function getUsersInRolePaginated(
roleId: IRole['_id'],
scope: string | undefined,
options?: any | undefined,
-): Promise> {
- return Roles.findUsersInRole(roleId, scope, options);
+): Promise>> {
+ if (process.env.NODE_ENV === 'development' && (scope === 'Users' || scope === 'Subscriptions')) {
+ throw new Error('Roles.findUsersInRole method received a role scope instead of a scope value.');
+ }
+
+ const role = await Roles.findOneById>(roleId, { projection: { scope: 1 } });
+ if (!role) {
+ throw new Error('role not found');
+ }
+
+ switch (role.scope) {
+ case 'Subscriptions':
+ const subscriptions = await Subscriptions.findByRolesAndRoomId({ roles: role._id, rid: scope }, { projection: { 'u._id': 1 } })
+ .map((subscription) => subscription.u?._id)
+ .toArray();
+
+ return Users.findPaginated({ _id: { $in: compact(subscriptions) } }, options || {});
+ case 'Users':
+ default:
+ return Users.findPaginatedUsersInRoles([role._id], options);
+ }
}
diff --git a/apps/meteor/app/authorization/server/functions/hasRole.ts b/apps/meteor/app/authorization/server/functions/hasRole.ts
index 9057072a2787..040d28d8a905 100644
--- a/apps/meteor/app/authorization/server/functions/hasRole.ts
+++ b/apps/meteor/app/authorization/server/functions/hasRole.ts
@@ -1,6 +1,5 @@
import type { IRole, IUser, IRoom, ISubscription } from '@rocket.chat/core-typings';
-
-import { Roles } from '../../../models/server/raw';
+import { Roles } from '@rocket.chat/models';
export const hasAnyRoleAsync = async (
userId: IUser['_id'],
diff --git a/apps/meteor/app/authorization/server/functions/upsertPermissions.ts b/apps/meteor/app/authorization/server/functions/upsertPermissions.ts
index 29ea924d7a18..837020591c01 100644
--- a/apps/meteor/app/authorization/server/functions/upsertPermissions.ts
+++ b/apps/meteor/app/authorization/server/functions/upsertPermissions.ts
@@ -1,9 +1,9 @@
/* eslint no-multi-spaces: 0 */
import type { IPermission, ISetting } from '@rocket.chat/core-typings';
+import { Permissions, Settings } from '@rocket.chat/models';
import { settings } from '../../../settings/server';
import { getSettingPermissionId, CONSTANTS } from '../../lib';
-import { Permissions, Settings } from '../../../models/server/raw';
import { createOrUpdateProtectedRoleAsync } from '../../../../server/lib/roles/createOrUpdateProtectedRole';
export const upsertPermissions = async (): Promise => {
@@ -224,6 +224,7 @@ export const upsertPermissions = async (): Promise => {
{ _id: 'remove-slackbridge-links', roles: ['admin'] },
{ _id: 'view-import-operations', roles: ['admin'] },
{ _id: 'clear-oembed-cache', roles: ['admin'] },
+ { _id: 'videoconf-ring-users', roles: ['admin', 'owner', 'moderator', 'user'] },
];
for await (const permission of permissions) {
diff --git a/apps/meteor/app/authorization/server/methods/addPermissionToRole.ts b/apps/meteor/app/authorization/server/methods/addPermissionToRole.ts
index 946a7813b226..8041cb8b7301 100644
--- a/apps/meteor/app/authorization/server/methods/addPermissionToRole.ts
+++ b/apps/meteor/app/authorization/server/methods/addPermissionToRole.ts
@@ -1,8 +1,8 @@
import { Meteor } from 'meteor/meteor';
+import { Permissions } from '@rocket.chat/models';
import { hasPermission } from '../functions/hasPermission';
import { CONSTANTS, AuthorizationUtils } from '../../lib';
-import { Permissions } from '../../../models/server/raw';
Meteor.methods({
async 'authorization:addPermissionToRole'(permissionId, role) {
diff --git a/apps/meteor/app/authorization/server/methods/addUserToRole.ts b/apps/meteor/app/authorization/server/methods/addUserToRole.ts
index 072f5162fcbd..c33f950e0dbc 100644
--- a/apps/meteor/app/authorization/server/methods/addUserToRole.ts
+++ b/apps/meteor/app/authorization/server/methods/addUserToRole.ts
@@ -1,12 +1,12 @@
import { Meteor } from 'meteor/meteor';
import _ from 'underscore';
import type { IRole, IUser, IRoom } from '@rocket.chat/core-typings';
+import { Roles } from '@rocket.chat/models';
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';
import { apiDeprecationLogger } from '../../../lib/server/lib/deprecationWarningLogger';
Meteor.methods({
diff --git a/apps/meteor/app/authorization/server/methods/deleteRole.ts b/apps/meteor/app/authorization/server/methods/deleteRole.ts
index 4d9fb5238fc9..71858b228705 100644
--- a/apps/meteor/app/authorization/server/methods/deleteRole.ts
+++ b/apps/meteor/app/authorization/server/methods/deleteRole.ts
@@ -1,7 +1,7 @@
import { Meteor } from 'meteor/meteor';
import type { IRole } from '@rocket.chat/core-typings';
+import { Roles } from '@rocket.chat/models';
-import { Roles } from '../../../models/server/raw';
import { hasPermission } from '../functions/hasPermission';
import { apiDeprecationLogger } from '../../../lib/server/lib/deprecationWarningLogger';
diff --git a/apps/meteor/app/authorization/server/methods/removeRoleFromPermission.ts b/apps/meteor/app/authorization/server/methods/removeRoleFromPermission.ts
index f690c29d5ea2..0d2ecf59333f 100644
--- a/apps/meteor/app/authorization/server/methods/removeRoleFromPermission.ts
+++ b/apps/meteor/app/authorization/server/methods/removeRoleFromPermission.ts
@@ -1,8 +1,8 @@
import { Meteor } from 'meteor/meteor';
+import { Permissions } from '@rocket.chat/models';
import { hasPermission } from '../functions/hasPermission';
import { CONSTANTS } from '../../lib';
-import { Permissions } from '../../../models/server/raw';
Meteor.methods({
async 'authorization:removeRoleFromPermission'(permissionId, role) {
diff --git a/apps/meteor/app/authorization/server/methods/removeUserFromRole.ts b/apps/meteor/app/authorization/server/methods/removeUserFromRole.ts
index 996c17f8b910..3eeda9c2c84e 100644
--- a/apps/meteor/app/authorization/server/methods/removeUserFromRole.ts
+++ b/apps/meteor/app/authorization/server/methods/removeUserFromRole.ts
@@ -1,12 +1,12 @@
import { Meteor } from 'meteor/meteor';
import _ from 'underscore';
import type { IRole, IUser } from '@rocket.chat/core-typings';
+import { Roles } from '@rocket.chat/models';
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';
import { apiDeprecationLogger } from '../../../lib/server/lib/deprecationWarningLogger';
Meteor.methods({
diff --git a/apps/meteor/app/authorization/server/methods/saveRole.ts b/apps/meteor/app/authorization/server/methods/saveRole.ts
index 3b0517852865..9eb19298f7f7 100644
--- a/apps/meteor/app/authorization/server/methods/saveRole.ts
+++ b/apps/meteor/app/authorization/server/methods/saveRole.ts
@@ -1,9 +1,9 @@
import { Meteor } from 'meteor/meteor';
import { isRoleCreateProps } from '@rocket.chat/rest-typings';
+import { Roles } from '@rocket.chat/models';
import { settings } from '../../../settings/server';
import { hasPermission } from '../functions/hasPermission';
-import { Roles } from '../../../models/server/raw';
import { methodDeprecationLogger } from '../../../lib/server/lib/deprecationWarningLogger';
import { updateRoleAsync } from '../../../../server/lib/roles/updateRole';
import { insertRoleAsync } from '../../../../server/lib/roles/insertRole';
@@ -13,16 +13,16 @@ Meteor.methods({
methodDeprecationLogger.warn('authorization:saveRole will be deprecated in future versions of Rocket.Chat');
const userId = Meteor.userId();
- if (!userId || !hasPermission(userId, 'access-permissions')) {
- throw new Meteor.Error('error-action-not-allowed', 'Accessing permissions is not allowed', {
+ if (!isRoleCreateProps(roleData)) {
+ throw new Meteor.Error('error-invalid-role-properties', 'The role properties are invalid.', {
method: 'authorization:saveRole',
- action: 'Accessing_permissions',
});
}
- if (!isRoleCreateProps(roleData)) {
- throw new Meteor.Error('error-invalid-role-properties', 'The role properties are invalid.', {
+ if (!userId || !hasPermission(userId, 'access-permissions')) {
+ throw new Meteor.Error('error-action-not-allowed', 'Accessing permissions is not allowed', {
method: 'authorization:saveRole',
+ action: 'Accessing_permissions',
});
}
diff --git a/apps/meteor/app/authorization/server/streamer/permissions/index.ts b/apps/meteor/app/authorization/server/streamer/permissions/index.ts
index 881f4f44e7b5..866bec5fb407 100644
--- a/apps/meteor/app/authorization/server/streamer/permissions/index.ts
+++ b/apps/meteor/app/authorization/server/streamer/permissions/index.ts
@@ -1,7 +1,6 @@
import { Meteor } from 'meteor/meteor';
import { check, Match } from 'meteor/check';
-
-import { Permissions } from '../../../../models/server/raw';
+import { Permissions } from '@rocket.chat/models';
Meteor.methods({
async 'permissions/get'(updatedAt: Date) {
diff --git a/apps/meteor/app/autotranslate/client/lib/actionButton.ts b/apps/meteor/app/autotranslate/client/lib/actionButton.ts
index 772917208147..0d742d68f9ab 100644
--- a/apps/meteor/app/autotranslate/client/lib/actionButton.ts
+++ b/apps/meteor/app/autotranslate/client/lib/actionButton.ts
@@ -6,7 +6,7 @@ import { AutoTranslate } from './autotranslate';
import { settings } from '../../../settings/client';
import { hasAtLeastOnePermission } from '../../../authorization/client';
import { MessageAction } from '../../../ui-utils/client/lib/MessageAction';
-import { messageArgs } from '../../../ui-utils/client/lib/messageArgs';
+import { messageArgs } from '../../../../client/lib/utils/messageArgs';
import { Messages } from '../../../models/client';
Meteor.startup(() => {
@@ -32,7 +32,7 @@ Meteor.startup(() => {
Messages.update({ _id: message._id }, { [action]: { autoTranslateShowInverse: true } });
},
condition({ message, user }) {
- return Boolean(message?.u && message.u._id !== user._id && isTranslatedMessage(message) && !message.translations.original);
+ return Boolean(message?.u && message.u._id !== user._id && isTranslatedMessage(message) && message.autoTranslateShowInverse);
},
order: 90,
});
@@ -54,7 +54,7 @@ Meteor.startup(() => {
Messages.update({ _id: message._id }, { [action]: { autoTranslateShowInverse: true } });
},
condition({ message, user }) {
- return Boolean(message?.u && message.u._id !== user._id && isTranslatedMessage(message) && message.translations.original);
+ return Boolean(message?.u && message.u._id !== user._id && isTranslatedMessage(message) && !message.autoTranslateShowInverse);
},
order: 90,
});
diff --git a/apps/meteor/app/autotranslate/client/lib/autotranslate.ts b/apps/meteor/app/autotranslate/client/lib/autotranslate.ts
index d7f8338de98f..20ead8d84dea 100644
--- a/apps/meteor/app/autotranslate/client/lib/autotranslate.ts
+++ b/apps/meteor/app/autotranslate/client/lib/autotranslate.ts
@@ -1,6 +1,5 @@
import { Meteor } from 'meteor/meteor';
import { Tracker } from 'meteor/tracker';
-import _ from 'underscore';
import mem from 'mem';
import { IRoom, ISubscription, ISupportedLanguage, ITranslatedMessage, IUser, MessageAttachmentDefault } from '@rocket.chat/core-typings';
@@ -24,7 +23,7 @@ Meteor.startup(() => {
export const AutoTranslate = {
initialized: false,
- providersMetadata: {},
+ providersMetadata: {} as { [providerNamer: string]: { name: string; displayName: string } },
messageIdsToWait: {} as { [messageId: string]: string },
supportedLanguages: [] as ISupportedLanguage[],
@@ -37,22 +36,38 @@ export const AutoTranslate = {
}
const language = (subscription?.autoTranslateLanguage || userLanguage || window.defaultUserLanguage?.()) as string;
if (language.indexOf('-') !== -1) {
- if (!_.findWhere(this.supportedLanguages, { language })) {
- return language.substr(0, 2);
+ if (!this.supportedLanguages.some((supportedLanguage) => supportedLanguage.language === language)) {
+ return language.slice(0, 2);
}
}
return language;
},
- translateAttachments(attachments: MessageAttachmentDefault[], language: string): MessageAttachmentDefault[] {
+ translateAttachments(
+ attachments: MessageAttachmentDefault[],
+ language: string,
+ autoTranslateShowInverse: boolean,
+ ): MessageAttachmentDefault[] {
for (const attachment of attachments) {
if (attachment.author_name !== username) {
if (attachment.text && attachment.translations && attachment.translations[language]) {
- attachment.text = attachment.translations[language];
+ attachment.translations.original = attachment.text;
+
+ if (autoTranslateShowInverse) {
+ attachment.text = attachment.translations.original;
+ } else {
+ attachment.text = attachment.translations[language];
+ }
}
if (attachment.description && attachment.translations && attachment.translations[language]) {
- attachment.description = attachment.translations[language];
+ attachment.translations.original = attachment.description;
+
+ if (autoTranslateShowInverse) {
+ attachment.description = attachment.translations.original;
+ } else {
+ attachment.description = attachment.translations[language];
+ }
}
// @ts-expect-error - not sure what to do with this
@@ -107,17 +122,33 @@ export const createAutoTranslateMessageRenderer = (): ((message: ITranslatedMess
message.translations = {};
}
if (!!subscription?.autoTranslate !== !!message.autoTranslateShowInverse) {
+ const hasAttachmentsTranslate =
+ message.attachments?.some(
+ (attachment) =>
+ 'translations' in attachment &&
+ typeof attachment.translations === 'object' &&
+ autoTranslateLanguage in attachment.translations,
+ ) ?? false;
+
message.translations.original = message.html;
- if (message.translations[autoTranslateLanguage]) {
+ if (message.translations[autoTranslateLanguage] && !hasAttachmentsTranslate) {
message.html = message.translations[autoTranslateLanguage];
}
if (message.attachments && message.attachments.length > 0) {
- message.attachments = AutoTranslate.translateAttachments(message.attachments, autoTranslateLanguage);
+ message.attachments = AutoTranslate.translateAttachments(
+ message.attachments,
+ autoTranslateLanguage,
+ !!message.autoTranslateShowInverse,
+ );
}
}
} else if (message.attachments && message.attachments.length > 0) {
- message.attachments = AutoTranslate.translateAttachments(message.attachments, autoTranslateLanguage);
+ message.attachments = AutoTranslate.translateAttachments(
+ message.attachments,
+ autoTranslateLanguage,
+ !!message.autoTranslateShowInverse,
+ );
}
return message;
};
diff --git a/apps/meteor/app/autotranslate/server/autotranslate.ts b/apps/meteor/app/autotranslate/server/autotranslate.ts
index a5f550d777d6..d50cb15a1457 100644
--- a/apps/meteor/app/autotranslate/server/autotranslate.ts
+++ b/apps/meteor/app/autotranslate/server/autotranslate.ts
@@ -308,6 +308,7 @@ export abstract class AutoTranslate {
const translations = this._translateAttachmentDescriptions(attachment, targetLanguages);
if (!_.isEmpty(translations)) {
Messages.addAttachmentTranslations(message._id, index, translations);
+ Messages.addTranslations(message._id, translations, TranslationProviderRegistry[Provider]);
}
}
}
diff --git a/apps/meteor/app/autotranslate/server/googleTranslate.ts b/apps/meteor/app/autotranslate/server/googleTranslate.ts
index 13a861532e38..e519f56fdbb8 100644
--- a/apps/meteor/app/autotranslate/server/googleTranslate.ts
+++ b/apps/meteor/app/autotranslate/server/googleTranslate.ts
@@ -94,7 +94,7 @@ class GoogleAutoTranslate extends AutoTranslate {
result = HTTP.get('https://translation.googleapis.com/language/translate/v2/languages', {
params,
});
- } catch (e) {
+ } catch (e: any) {
// Fallback: Get the English names of the target languages
if (
e.response &&
diff --git a/apps/meteor/app/autotranslate/server/permissions.ts b/apps/meteor/app/autotranslate/server/permissions.ts
index cf712cc6d552..f75edf705c24 100644
--- a/apps/meteor/app/autotranslate/server/permissions.ts
+++ b/apps/meteor/app/autotranslate/server/permissions.ts
@@ -1,6 +1,5 @@
import { Meteor } from 'meteor/meteor';
-
-import { Permissions } from '../../models/server/raw';
+import { Permissions } from '@rocket.chat/models';
Meteor.startup(async () => {
if (!(await Permissions.findOne({ _id: 'auto-translate' }))) {
diff --git a/apps/meteor/app/bigbluebutton/server/bigbluebutton-api.js b/apps/meteor/app/bigbluebutton/server/bigbluebutton-api.js
deleted file mode 100644
index 8cb3f4d447c4..000000000000
--- a/apps/meteor/app/bigbluebutton/server/bigbluebutton-api.js
+++ /dev/null
@@ -1,188 +0,0 @@
-/* eslint-disable */
-import crypto from 'crypto';
-import { SystemLogger } from '../../../server/lib/logger/system';
-
-var BigBlueButtonApi, filterCustomParameters, include, noChecksumMethods,
- __indexOf = [].indexOf || function (item) { for (var i = 0, l = this.length; i < l; i++) { if (i in this && this[i] === item) return i; } return -1; };
-
-BigBlueButtonApi = (function () {
- function BigBlueButtonApi(url, salt, debug, opts) {
- var _base;
- if (opts == null) {
- opts = {};
- }
- this.url = url;
- this.salt = salt;
- this.opts = opts;
- if ((_base = this.opts).shaType == null) {
- _base.shaType = 'sha1';
- }
- }
-
- BigBlueButtonApi.prototype.availableApiCalls = function () {
- return ['/', 'create', 'join', 'isMeetingRunning', 'getMeetingInfo', 'end', 'getMeetings', 'getDefaultConfigXML', 'setConfigXML', 'enter', 'configXML', 'signOut', 'getRecordings', 'publishRecordings', 'deleteRecordings', 'updateRecordings', 'hooks/create'];
- };
-
- BigBlueButtonApi.prototype.urlParamsFor = function (param) {
- switch (param) {
- case "create":
- return [["meetingID", true], ["name", true], ["attendeePW", false], ["moderatorPW", false], ["welcome", false], ["dialNumber", false], ["voiceBridge", false], ["webVoice", false], ["logoutURL", false], ["maxParticipants", false], ["record", false], ["duration", false], ["moderatorOnlyMessage", false], ["autoStartRecording", false], ["allowStartStopRecording", false], [/meta_\w+/, false]];
- case "join":
- return [["fullName", true], ["meetingID", true], ["password", true], ["createTime", false], ["userID", false], ["webVoiceConf", false], ["configToken", false], ["avatarURL", false], ["redirect", false], ["clientURL", false]];
- case "isMeetingRunning":
- return [["meetingID", true]];
- case "end":
- return [["meetingID", true], ["password", true]];
- case "getMeetingInfo":
- return [["meetingID", true], ["password", true]];
- case "getRecordings":
- return [["meetingID", false], ["recordID", false], ["state", false], [/meta_\w+/, false]];
- case "publishRecordings":
- return [["recordID", true], ["publish", true]];
- case "deleteRecordings":
- return [["recordID", true]];
- case "updateRecordings":
- return [["recordID", true], [/meta_\w+/, false]];
- case "hooks/create":
- return [["callbackURL", false], ["meetingID", false]];
- }
- };
-
- BigBlueButtonApi.prototype.filterParams = function (params, method) {
- var filters, r;
- filters = this.urlParamsFor(method);
- if ((filters == null) || filters.length === 0) {
- ({});
- } else {
- r = include(params, function (key, value) {
- var filter, _i, _len;
- for (_i = 0, _len = filters.length; _i < _len; _i++) {
- filter = filters[_i];
- if (filter[0] instanceof RegExp) {
- if (key.match(filter[0]) || key.match(/^custom_/)) {
- return true;
- }
- } else {
- if (key.match("^" + filter[0] + "$") || key.match(/^custom_/)) {
- return true;
- }
- }
- }
- return false;
- });
- }
- return filterCustomParameters(r);
- };
-
- BigBlueButtonApi.prototype.urlFor = function (method, params, filter) {
- var checksum, key, keys, param, paramList, property, query, sep, url, _i, _len;
- if (filter == null) {
- filter = true;
- }
- SystemLogger.debug("Generating URL for", method);
- if (filter) {
- params = this.filterParams(params, method);
- } else {
- params = filterCustomParameters(params);
- }
- url = this.url;
- paramList = [];
- if (params != null) {
- keys = [];
- for (property in params) {
- keys.push(property);
- }
- keys = keys.sort();
- for (_i = 0, _len = keys.length; _i < _len; _i++) {
- key = keys[_i];
- if (key != null) {
- param = params[key];
- }
- if (param != null) {
- paramList.push("" + (this.encodeForUrl(key)) + "=" + (this.encodeForUrl(param)));
- }
- }
- if (paramList.length > 0) {
- query = paramList.join("&");
- }
- } else {
- query = '';
- }
- checksum = this.checksum(method, query);
- if (paramList.length > 0) {
- query = "" + method + "?" + query;
- sep = '&';
- } else {
- if (method !== '/') {
- query = method;
- }
- sep = '?';
- }
- if (__indexOf.call(noChecksumMethods(), method) < 0) {
- query = "" + query + sep + "checksum=" + checksum;
- }
- return "" + url + "/" + query;
- };
-
- BigBlueButtonApi.prototype.checksum = function (method, query) {
- var c, shaObj, str;
- query || (query = "");
- SystemLogger.debug("- Calculating the checksum using: '" + method + "', '" + query + "', '" + this.salt + "'");
- str = method + query + this.salt;
- if (this.opts.shaType === 'sha256') {
- shaObj = crypto.createHash('sha256', "TEXT")
- } else {
- shaObj = crypto.createHash('sha1', "TEXT")
- }
- shaObj.update(str);
- c = shaObj.digest('hex');
- SystemLogger.debug("- Checksum calculated:", c);
- return c;
- };
-
- BigBlueButtonApi.prototype.encodeForUrl = function (value) {
- return encodeURIComponent(value).replace(/%20/g, '+').replace(/[!'()]/g, escape).replace(/\*/g, "%2A");
- };
-
- BigBlueButtonApi.prototype.setMobileProtocol = function (url) {
- return url.replace(/http[s]?\:\/\//, "bigbluebutton://");
- };
-
- return BigBlueButtonApi;
-
-})();
-
-include = function (input, _function) {
- var key, value, _match, _obj;
- _obj = new Object;
- _match = null;
- for (key in input) {
- value = input[key];
- if (_function.call(input, key, value)) {
- _obj[key] = value;
- }
- }
- return _obj;
-};
-
-export default BigBlueButtonApi;
-
-filterCustomParameters = function (params) {
- var key, v;
- for (key in params) {
- v = params[key];
- if (key.match(/^custom_/)) {
- params[key.replace(/^custom_/, "")] = v;
- }
- }
- for (key in params) {
- if (key.match(/^custom_/)) {
- delete params[key];
- }
- }
- return params;
-};
-
-noChecksumMethods = function () {
- return ['setConfigXML', '/', 'enter', 'configXML', 'signOut'];
-};
diff --git a/apps/meteor/app/bigbluebutton/server/index.js b/apps/meteor/app/bigbluebutton/server/index.js
deleted file mode 100644
index b6be696a20bd..000000000000
--- a/apps/meteor/app/bigbluebutton/server/index.js
+++ /dev/null
@@ -1 +0,0 @@
-export { default } from './bigbluebutton-api';
diff --git a/apps/meteor/app/blockstack/client/index.js b/apps/meteor/app/blockstack/client/index.js
deleted file mode 100644
index 23420dbe4d70..000000000000
--- a/apps/meteor/app/blockstack/client/index.js
+++ /dev/null
@@ -1,53 +0,0 @@
-import { Meteor } from 'meteor/meteor';
-import { ServiceConfiguration } from 'meteor/service-configuration';
-import { check, Match } from 'meteor/check';
-import { Session } from 'meteor/session';
-import './routes';
-
-const handleError = (error) => error && Session.set('errorMessage', error.reason || 'Unknown error');
-
-// TODO: allow serviceConfig.loginStyle == popup
-Meteor.loginWithBlockstack = (options, callback = handleError) => {
- if (!options || !options.redirectURI) {
- options = ServiceConfiguration.configurations.findOne({
- service: 'blockstack',
- });
-
- options.blockstackIDHost = Meteor.Device.isDesktop() ? 'http://localhost:8888/auth' : 'https://blockstack.org/auth';
-
- options.scopes = ['store_write'];
- }
-
- try {
- check(
- options,
- Match.ObjectIncluding({
- blockstackIDHost: String,
- redirectURI: String,
- manifestURI: String,
- }),
- );
-
- import('blockstack/dist/blockstack').then(({ redirectToSignIn }) =>
- redirectToSignIn(options.redirectURI, options.manifestURI, options.scopes),
- );
- } catch (err) {
- callback.call(Meteor, err);
- }
-};
-
-const meteorLogout = Meteor.logout;
-Meteor.logout = (...args) => {
- const serviceConfig = ServiceConfiguration.configurations.findOne({
- service: 'blockstack',
- });
-
- const blockstackAuth = Session.get('blockstack_auth');
-
- if (serviceConfig && blockstackAuth) {
- Session.delete('blockstack_auth');
- import('blockstack/dist/blockstack').then(({ signUserOut }) => signUserOut(window.location.href));
- }
-
- return meteorLogout(...args);
-};
diff --git a/apps/meteor/app/blockstack/client/routes.js b/apps/meteor/app/blockstack/client/routes.js
deleted file mode 100644
index 1f0e340c5eda..000000000000
--- a/apps/meteor/app/blockstack/client/routes.js
+++ /dev/null
@@ -1,46 +0,0 @@
-import { Meteor } from 'meteor/meteor';
-import { Accounts } from 'meteor/accounts-base';
-import { FlowRouter } from 'meteor/kadira:flow-router';
-
-const blockstackLogin = (authResponse, userData = {}) => {
- Accounts.callLoginMethod({
- methodArguments: [
- {
- blockstack: true,
- authResponse,
- userData,
- },
- ],
- userCallback() {
- FlowRouter.go('home');
- },
- });
-};
-
-FlowRouter.route('/_blockstack/validate', {
- name: 'blockstackValidate',
- async action(params, queryParams) {
- const blockstack = await import('blockstack/dist/blockstack');
-
- if (Meteor.userId()) {
- console.log('Blockstack Auth requested when already logged in. Reloading.');
- return FlowRouter.go('home');
- }
-
- if (queryParams.authResponse == null) {
- throw new Meteor.Error('Blockstack: Auth request without response param.');
- }
-
- let userData;
-
- if (blockstack.isUserSignedIn()) {
- userData = blockstack.loadUserData();
- }
-
- if (blockstack.isSignInPending()) {
- userData = await blockstack.handlePendingSignIn();
- }
-
- blockstackLogin(queryParams.authResponse, userData);
- },
-});
diff --git a/apps/meteor/app/blockstack/server/index.js b/apps/meteor/app/blockstack/server/index.js
deleted file mode 100644
index f0cf809aaf0e..000000000000
--- a/apps/meteor/app/blockstack/server/index.js
+++ /dev/null
@@ -1,3 +0,0 @@
-import './routes.js';
-import './settings.js';
-import './loginHandler.js';
diff --git a/apps/meteor/app/blockstack/server/logger.js b/apps/meteor/app/blockstack/server/logger.js
deleted file mode 100644
index e88f4df9bf1c..000000000000
--- a/apps/meteor/app/blockstack/server/logger.js
+++ /dev/null
@@ -1,3 +0,0 @@
-import { Logger } from '../../logger';
-
-export const logger = new Logger('Blockstack');
diff --git a/apps/meteor/app/blockstack/server/loginHandler.js b/apps/meteor/app/blockstack/server/loginHandler.js
deleted file mode 100644
index c1f50416d5c8..000000000000
--- a/apps/meteor/app/blockstack/server/loginHandler.js
+++ /dev/null
@@ -1,56 +0,0 @@
-import { Meteor } from 'meteor/meteor';
-import { Accounts } from 'meteor/accounts-base';
-
-import { updateOrCreateUser } from './userHandler';
-import { handleAccessToken } from './tokenHandler';
-import { logger } from './logger';
-import { settings } from '../../settings/server';
-import { Users } from '../../models';
-import { setUserAvatar } from '../../lib';
-
-// Blockstack login handler, triggered by a blockstack authResponse in route
-Accounts.registerLoginHandler('blockstack', (loginRequest) => {
- if (!loginRequest.blockstack || !loginRequest.authResponse) {
- return;
- }
-
- if (!settings.get('Blockstack_Enable')) {
- return;
- }
-
- logger.debug('Processing login request', loginRequest);
-
- const auth = handleAccessToken(loginRequest);
-
- // TODO: Fix #9484 and re-instate usage of accounts helper
- // const result = Accounts.updateOrCreateUserFromExternalService('blockstack', auth.serviceData, auth.options)
- const result = updateOrCreateUser(auth.serviceData, auth.options);
- logger.debug('User create/update result', result);
-
- // Ensure processing succeeded
- if (result === undefined || result.userId === undefined) {
- return {
- type: 'blockstack',
- error: new Meteor.Error(Accounts.LoginCancelledError.numericError, 'User creation failed from Blockstack response token'),
- };
- }
-
- if (result.isNew) {
- try {
- const user = Users.findOneById(result.userId, {
- fields: { 'services.blockstack.image': 1, 'username': 1 },
- });
- if (user && user.services && user.services.blockstack && user.services.blockstack.image) {
- Meteor.runAsUser(user._id, () => {
- setUserAvatar(user, user.services.blockstack.image, undefined, 'url');
- });
- }
- } catch (e) {
- logger.error(e);
- }
- }
-
- delete result.isNew;
-
- return result;
-});
diff --git a/apps/meteor/app/blockstack/server/routes.js b/apps/meteor/app/blockstack/server/routes.js
deleted file mode 100644
index 9f6bc061f787..000000000000
--- a/apps/meteor/app/blockstack/server/routes.js
+++ /dev/null
@@ -1,31 +0,0 @@
-import { Meteor } from 'meteor/meteor';
-import { WebApp } from 'meteor/webapp';
-
-import { settings } from '../../settings/server';
-import { RocketChatAssets } from '../../assets/server';
-
-WebApp.connectHandlers.use(
- '/_blockstack/manifest',
- Meteor.bindEnvironment(function (req, res) {
- const name = settings.get('Site_Name');
- const startUrl = Meteor.absoluteUrl();
- const description = settings.get('Blockstack_Auth_Description');
- const iconUrl = RocketChatAssets.getURL('Assets_favicon_192');
-
- res.writeHead(200, {
- 'Content-Type': 'application/json',
- 'Access-Control-Allow-Origin': '*',
- });
-
- res.end(`{
- "name": "${name}",
- "start_url": "${startUrl}",
- "description": "${description}",
- "icons": [{
- "src": "${iconUrl}",
- "sizes": "192x192",
- "type": "image/png"
- }]
- }`);
- }),
-);
diff --git a/apps/meteor/app/blockstack/server/settings.js b/apps/meteor/app/blockstack/server/settings.js
deleted file mode 100644
index 640e35187a6b..000000000000
--- a/apps/meteor/app/blockstack/server/settings.js
+++ /dev/null
@@ -1,70 +0,0 @@
-import { Meteor } from 'meteor/meteor';
-import { ServiceConfiguration } from 'meteor/service-configuration';
-
-import { logger } from './logger';
-import { settings, settingsRegistry } from '../../settings/server';
-
-const defaults = {
- enable: false,
- loginStyle: 'redirect',
- generateUsername: false,
- manifestURI: Meteor.absoluteUrl('_blockstack/manifest'),
- redirectURI: Meteor.absoluteUrl('_blockstack/validate'),
- authDescription: 'Rocket.Chat login',
- buttonLabelText: 'Blockstack',
- buttonColor: '#271132',
- buttonLabelColor: '#ffffff',
-};
-
-Meteor.startup(() => {
- settingsRegistry.addGroup('Blockstack', function () {
- this.add('Blockstack_Enable', defaults.enable, {
- type: 'boolean',
- i18nLabel: 'Enable',
- });
- this.add('Blockstack_Auth_Description', defaults.authDescription, {
- type: 'string',
- });
- this.add('Blockstack_ButtonLabelText', defaults.buttonLabelText, {
- type: 'string',
- });
- this.add('Blockstack_Generate_Username', defaults.generateUsername, {
- type: 'boolean',
- });
- });
-});
-
-// Helper to return all Blockstack settings
-const getSettings = () =>
- Object.assign({}, defaults, {
- enable: settings.get('Blockstack_Enable'),
- authDescription: settings.get('Blockstack_Auth_Description'),
- buttonLabelText: settings.get('Blockstack_ButtonLabelText'),
- generateUsername: settings.get('Blockstack_Generate_Username'),
- });
-
-// Add settings to auth provider configs on startup
-settings.watchMultiple(
- ['Blockstack_Enable', 'Blockstack_Auth_Description', 'Blockstack_ButtonLabelText', 'Blockstack_Generate_Username'],
- () => {
- const serviceConfig = getSettings();
-
- if (!serviceConfig.enable) {
- logger.debug('Blockstack not enabled', serviceConfig);
- return ServiceConfiguration.configurations.remove({
- service: 'blockstack',
- });
- }
-
- ServiceConfiguration.configurations.upsert(
- {
- service: 'blockstack',
- },
- {
- $set: serviceConfig,
- },
- );
-
- logger.debug('Init Blockstack auth', serviceConfig);
- },
-);
diff --git a/apps/meteor/app/blockstack/server/tokenHandler.js b/apps/meteor/app/blockstack/server/tokenHandler.js
deleted file mode 100644
index 29079e3eb0a3..000000000000
--- a/apps/meteor/app/blockstack/server/tokenHandler.js
+++ /dev/null
@@ -1,63 +0,0 @@
-import { decodeToken } from 'blockstack';
-import { Meteor } from 'meteor/meteor';
-import { Accounts } from 'meteor/accounts-base';
-import { Match, check } from 'meteor/check';
-
-import { logger } from './logger';
-
-// Handler extracts data from JSON and tokenised reponse.
-// Reflects OAuth token service, with some slight modifications for Blockstack.
-//
-// Uses 'iss' (issuer) as unique key (decentralised ID) for user.
-// The 'did' final portion of the blockstack decentralised ID, is displayed as
-// your profile ID in the service. This isn't used yet, but could be useful
-// to link accounts if identity providers other than btc address are added.
-export const handleAccessToken = (loginRequest) => {
- logger.debug('Login request received', loginRequest);
-
- check(
- loginRequest,
- Match.ObjectIncluding({
- authResponse: String,
- userData: Object,
- }),
- );
-
- // Decode auth response for user attributes
- const { username, profile } = loginRequest.userData;
- const decodedToken = decodeToken(loginRequest.authResponse).payload;
-
- profile.username = username;
-
- logger.debug('User data', loginRequest.userData);
- logger.debug('Login decoded', decodedToken);
-
- const { iss, iat, exp } = decodedToken;
-
- if (!iss) {
- return {
- type: 'blockstack',
- error: new Meteor.Error(Accounts.LoginCancelledError.numericError, 'Insufficient data in auth response token'),
- };
- }
-
- // Collect basic auth provider details
- const serviceData = {
- id: iss,
- did: iss.split(':').pop(),
- issuedAt: new Date(iat * 1000),
- expiresAt: new Date(exp * 1000),
- };
-
- // Add Avatar image source to use for auth service suggestions
- if (Array.isArray(profile.image) && profile.image.length) {
- serviceData.image = profile.image[0].contentUrl;
- }
-
- logger.debug('Login data', serviceData, profile);
-
- return {
- serviceData,
- options: { profile },
- };
-};
diff --git a/apps/meteor/app/blockstack/server/userHandler.js b/apps/meteor/app/blockstack/server/userHandler.js
deleted file mode 100644
index 393129f74a99..000000000000
--- a/apps/meteor/app/blockstack/server/userHandler.js
+++ /dev/null
@@ -1,81 +0,0 @@
-import { Meteor } from 'meteor/meteor';
-import { Accounts } from 'meteor/accounts-base';
-import { ServiceConfiguration } from 'meteor/service-configuration';
-
-import { logger } from './logger';
-import { settings } from '../../settings/server';
-import { generateUsernameSuggestion } from '../../lib';
-
-// Updates or creates a user after we authenticate with Blockstack
-// Clones Accounts.updateOrCreateUserFromExternalService with some modifications
-export const updateOrCreateUser = (serviceData, options) => {
- const serviceConfig = ServiceConfiguration.configurations.findOne({ service: 'blockstack' });
- logger.debug('Auth config', serviceConfig);
-
- // Extract user data from service / token
- const { id, did } = serviceData;
- const { profile } = options;
-
- // Look for existing Blockstack user
- const user = Meteor.users.findOne({ 'services.blockstack.id': id });
- let userId;
- let isNew = false;
-
- // Use found or create a user
- if (user) {
- logger.info(`User login with Blockstack ID ${id}`);
- userId = user._id;
- } else {
- isNew = true;
- let emails = [];
- if (!Array.isArray(profile.emails)) {
- // Fix absense of emails by adding placeholder address using decentralised
- // ID at blockstack.email - a holding domain only, no MX record, does not
- // process email, may be used in future to provide decentralised email via
- // gaia, encrypting mail for DID user only. @TODO: document this approach.
- emails.push({ address: `${did}@blockstack.email`, verified: false });
- } else {
- const verified = settings.get('Accounts_Verify_Email_For_External_Accounts');
- // Reformat array of emails into expected format if they exist
- emails = profile.emails.map((address) => ({ address, verified }));
- }
-
- const newUser = {
- name: profile.name,
- active: true,
- emails,
- services: { blockstack: serviceData },
- };
-
- // Set username same as in blockstack, or suggest if none
- if (profile.name) {
- newUser.name = profile.name;
- }
-
- // Take profile username if exists, or generate one if enabled
- if (profile.username && profile.username !== '') {
- newUser.username = profile.username;
- } else if (serviceConfig.generateUsername === true) {
- newUser.username = generateUsernameSuggestion(newUser);
- }
- // If no username at this point it will suggest one from the name
-
- // Create and get created user to make a couple more mods before returning
- logger.info(`Creating user for Blockstack ID ${id}`);
- userId = Accounts.insertUserDoc({}, newUser);
- logger.debug('New user ${ userId }', newUser);
- }
-
- // Add login token for blockstack auth session (take expiration from response)
- // TODO: Regquired method result format ignores `.when`
- const { token } = Accounts._generateStampedLoginToken();
- const tokenExpires = serviceData.expiresAt;
-
- return {
- type: 'blockstack',
- userId,
- token,
- tokenExpires,
- isNew,
- };
-};
diff --git a/apps/meteor/app/cas/server/cas_server.js b/apps/meteor/app/cas/server/cas_server.js
index 9ba95cd943bf..cf0a273c4fcc 100644
--- a/apps/meteor/app/cas/server/cas_server.js
+++ b/apps/meteor/app/cas/server/cas_server.js
@@ -6,12 +6,12 @@ import { WebApp } from 'meteor/webapp';
import { RoutePolicy } from 'meteor/routepolicy';
import _ from 'underscore';
import fiber from 'fibers';
-import CAS from 'cas';
+import { CredentialTokens } from '@rocket.chat/models';
+import { validate } from '@rocket.chat/cas-validate';
import { logger } from './cas_rocketchat';
-import { settings } from '../../settings';
+import { settings } from '../../settings/server';
import { Rooms } from '../../models/server';
-import { CredentialTokens } from '../../models/server/raw';
import { _setRealName } from '../../lib';
import { createRoom } from '../../lib/server/functions/createRoom';
@@ -38,13 +38,12 @@ const casTicket = function (req, token, callback) {
const appUrl = Meteor.absoluteUrl().replace(/\/$/, '') + __meteor_runtime_config__.ROOT_URL_PATH_PREFIX;
logger.debug(`Using CAS_base_url: ${baseUrl}`);
- const cas = new CAS({
- base_url: baseUrl,
- version: cas_version,
- service: `${appUrl}/_cas/${token}`,
- });
-
- cas.validate(
+ validate(
+ {
+ base_url: baseUrl,
+ version: cas_version,
+ service: `${appUrl}/_cas/${token}`,
+ },
ticketId,
Meteor.bindEnvironment(async function (err, status, username, details) {
if (err) {
diff --git a/apps/meteor/app/channel-settings/server/functions/saveReactWhenReadOnly.js b/apps/meteor/app/channel-settings/server/functions/saveReactWhenReadOnly.js
index d4a6898678d6..8b118c51f01e 100644
--- a/apps/meteor/app/channel-settings/server/functions/saveReactWhenReadOnly.js
+++ b/apps/meteor/app/channel-settings/server/functions/saveReactWhenReadOnly.js
@@ -1,7 +1,7 @@
import { Meteor } from 'meteor/meteor';
import { Match } from 'meteor/check';
-import { Rooms, Messages } from '../../../models';
+import { Rooms, Messages } from '../../../models/server';
export const saveReactWhenReadOnly = function (rid, allowReact, user, sendMessage = true) {
if (!Match.test(rid, String)) {
diff --git a/apps/meteor/app/channel-settings/server/functions/saveRoomAnnouncement.js b/apps/meteor/app/channel-settings/server/functions/saveRoomAnnouncement.js
index 38a5f56d612a..b3f66bf311a0 100644
--- a/apps/meteor/app/channel-settings/server/functions/saveRoomAnnouncement.js
+++ b/apps/meteor/app/channel-settings/server/functions/saveRoomAnnouncement.js
@@ -1,7 +1,7 @@
import { Meteor } from 'meteor/meteor';
import { Match } from 'meteor/check';
-import { Rooms, Messages } from '../../../models';
+import { Rooms, Messages } from '../../../models/server';
export const saveRoomAnnouncement = function (rid, roomAnnouncement, user, sendMessage = true) {
if (!Match.test(rid, String)) {
diff --git a/apps/meteor/app/channel-settings/server/functions/saveRoomCustomFields.js b/apps/meteor/app/channel-settings/server/functions/saveRoomCustomFields.js
index 5691cd69b169..c246e01224ba 100644
--- a/apps/meteor/app/channel-settings/server/functions/saveRoomCustomFields.js
+++ b/apps/meteor/app/channel-settings/server/functions/saveRoomCustomFields.js
@@ -1,7 +1,7 @@
import { Meteor } from 'meteor/meteor';
import { Match } from 'meteor/check';
-import { Rooms, Subscriptions } from '../../../models';
+import { Rooms, Subscriptions } from '../../../models/server';
export const saveRoomCustomFields = function (rid, roomCustomFields) {
if (!Match.test(rid, String)) {
diff --git a/apps/meteor/app/channel-settings/server/functions/saveRoomDescription.js b/apps/meteor/app/channel-settings/server/functions/saveRoomDescription.js
index 0804175f8e2a..dd6e0fb3ee1b 100644
--- a/apps/meteor/app/channel-settings/server/functions/saveRoomDescription.js
+++ b/apps/meteor/app/channel-settings/server/functions/saveRoomDescription.js
@@ -1,7 +1,7 @@
import { Meteor } from 'meteor/meteor';
import { Match } from 'meteor/check';
-import { Rooms, Messages } from '../../../models';
+import { Rooms, Messages } from '../../../models/server';
export const saveRoomDescription = function (rid, roomDescription, user) {
if (!Match.test(rid, String)) {
diff --git a/apps/meteor/app/channel-settings/server/functions/saveRoomEncrypted.ts b/apps/meteor/app/channel-settings/server/functions/saveRoomEncrypted.ts
index f0ccf1eb2164..7d6a906ab8f1 100644
--- a/apps/meteor/app/channel-settings/server/functions/saveRoomEncrypted.ts
+++ b/apps/meteor/app/channel-settings/server/functions/saveRoomEncrypted.ts
@@ -1,11 +1,11 @@
import { Meteor } from 'meteor/meteor';
import { Match } from 'meteor/check';
-import type { WriteOpResult } from 'mongodb';
+import type { UpdateResult } from 'mongodb';
import type { IUser } from '@rocket.chat/core-typings';
import { Rooms, Messages } from '../../../models/server';
-export const saveRoomEncrypted = function (rid: string, encrypted: boolean, user: IUser, sendMessage = true): Promise {
+export const saveRoomEncrypted = function (rid: string, encrypted: boolean, user: IUser, sendMessage = true): Promise {
if (!Match.test(rid, String)) {
throw new Meteor.Error('invalid-room', 'Invalid room', {
function: 'RocketChat.saveRoomEncrypted',
diff --git a/apps/meteor/app/channel-settings/server/functions/saveRoomName.js b/apps/meteor/app/channel-settings/server/functions/saveRoomName.js
index 05615a865075..941f8e86fcfc 100644
--- a/apps/meteor/app/channel-settings/server/functions/saveRoomName.js
+++ b/apps/meteor/app/channel-settings/server/functions/saveRoomName.js
@@ -1,7 +1,7 @@
import { Meteor } from 'meteor/meteor';
+import { Integrations } from '@rocket.chat/models';
import { Rooms, Messages, Subscriptions } from '../../../models/server';
-import { Integrations } from '../../../models/server/raw';
import { getValidRoomName } from '../../../utils/server';
import { callbacks } from '../../../../lib/callbacks';
import { checkUsernameAvailability } from '../../../lib/server/functions';
diff --git a/apps/meteor/app/channel-settings/server/functions/saveRoomReadOnly.js b/apps/meteor/app/channel-settings/server/functions/saveRoomReadOnly.js
index 7b7ed7bd0404..7cdb614e0679 100644
--- a/apps/meteor/app/channel-settings/server/functions/saveRoomReadOnly.js
+++ b/apps/meteor/app/channel-settings/server/functions/saveRoomReadOnly.js
@@ -1,7 +1,7 @@
import { Meteor } from 'meteor/meteor';
import { Match } from 'meteor/check';
-import { Rooms, Messages } from '../../../models';
+import { Rooms, Messages } from '../../../models/server';
export const saveRoomReadOnly = function (rid, readOnly, user, sendMessage = true) {
if (!Match.test(rid, String)) {
diff --git a/apps/meteor/app/channel-settings/server/functions/saveRoomSystemMessages.js b/apps/meteor/app/channel-settings/server/functions/saveRoomSystemMessages.js
index 200b2d4eea8d..f3e30630ce5c 100644
--- a/apps/meteor/app/channel-settings/server/functions/saveRoomSystemMessages.js
+++ b/apps/meteor/app/channel-settings/server/functions/saveRoomSystemMessages.js
@@ -1,7 +1,7 @@
import { Meteor } from 'meteor/meteor';
import { Match } from 'meteor/check';
-import { Rooms } from '../../../models';
+import { Rooms } from '../../../models/server';
import { MessageTypesValues } from '../../../lib/lib/MessageTypes';
export const saveRoomSystemMessages = function (rid, systemMessages) {
diff --git a/apps/meteor/app/channel-settings/server/functions/saveRoomTokens.js b/apps/meteor/app/channel-settings/server/functions/saveRoomTokens.js
deleted file mode 100644
index 400da0386611..000000000000
--- a/apps/meteor/app/channel-settings/server/functions/saveRoomTokens.js
+++ /dev/null
@@ -1,14 +0,0 @@
-import { Meteor } from 'meteor/meteor';
-import { Match } from 'meteor/check';
-
-import { Rooms } from '../../../models';
-
-export const saveRoomTokenpass = function (rid, tokenpass) {
- if (!Match.test(rid, String)) {
- throw new Meteor.Error('invalid-room', 'Invalid room', {
- function: 'RocketChat.saveRoomTokens',
- });
- }
-
- return Rooms.setTokenpassById(rid, tokenpass);
-};
diff --git a/apps/meteor/app/channel-settings/server/functions/saveRoomTopic.js b/apps/meteor/app/channel-settings/server/functions/saveRoomTopic.js
index 51a5a05035e2..5edf3c215f31 100644
--- a/apps/meteor/app/channel-settings/server/functions/saveRoomTopic.js
+++ b/apps/meteor/app/channel-settings/server/functions/saveRoomTopic.js
@@ -1,7 +1,8 @@
import { Meteor } from 'meteor/meteor';
import { Match } from 'meteor/check';
-import { Rooms, Messages } from '../../../models';
+import { Rooms, Messages } from '../../../models/server';
+import { callbacks } from '../../../../lib/callbacks';
export const saveRoomTopic = function (rid, roomTopic, user, sendMessage = true) {
if (!Match.test(rid, String)) {
@@ -14,5 +15,6 @@ export const saveRoomTopic = function (rid, roomTopic, user, sendMessage = true)
if (update && sendMessage) {
Messages.createRoomSettingsChangedWithTypeRoomIdMessageAndUser('room_changed_topic', rid, roomTopic, user);
}
+ callbacks.run('afterRoomTopicChange', { rid, topic: roomTopic });
return update;
};
diff --git a/apps/meteor/app/channel-settings/server/functions/saveStreamingOptions.js b/apps/meteor/app/channel-settings/server/functions/saveStreamingOptions.js
index c5c2d2b8fb2b..b9b7cea7f5f7 100644
--- a/apps/meteor/app/channel-settings/server/functions/saveStreamingOptions.js
+++ b/apps/meteor/app/channel-settings/server/functions/saveStreamingOptions.js
@@ -1,7 +1,7 @@
import { Meteor } from 'meteor/meteor';
import { Match, check } from 'meteor/check';
-import { Rooms } from '../../../models';
+import { Rooms } from '../../../models/server';
export const saveStreamingOptions = function (rid, options) {
if (!Match.test(rid, String)) {
diff --git a/apps/meteor/app/channel-settings/server/methods/saveRoomSettings.js b/apps/meteor/app/channel-settings/server/methods/saveRoomSettings.js
index 3462c720a570..89dcd43fb8b8 100644
--- a/apps/meteor/app/channel-settings/server/methods/saveRoomSettings.js
+++ b/apps/meteor/app/channel-settings/server/methods/saveRoomSettings.js
@@ -1,10 +1,10 @@
import { Meteor } from 'meteor/meteor';
-import { Match, check } from 'meteor/check';
+import { Match } from 'meteor/check';
import { TEAM_TYPE } from '@rocket.chat/core-typings';
import { setRoomAvatar } from '../../../lib/server/functions/setRoomAvatar';
import { hasPermission } from '../../../authorization';
-import { Rooms } from '../../../models';
+import { Rooms } from '../../../models/server';
import { callbacks } from '../../../../lib/callbacks';
import { saveRoomName } from '../functions/saveRoomName';
import { saveRoomTopic } from '../functions/saveRoomTopic';
@@ -15,7 +15,6 @@ import { saveRoomType } from '../functions/saveRoomType';
import { saveRoomReadOnly } from '../functions/saveRoomReadOnly';
import { saveReactWhenReadOnly } from '../functions/saveReactWhenReadOnly';
import { saveRoomSystemMessages } from '../functions/saveRoomSystemMessages';
-import { saveRoomTokenpass } from '../functions/saveRoomTokens';
import { saveRoomEncrypted } from '../functions/saveRoomEncrypted';
import { saveStreamingOptions } from '../functions/saveStreamingOptions';
import { Team } from '../../../../server/sdk';
@@ -36,7 +35,6 @@ const fields = [
'systemMessages',
'default',
'joinCode',
- 'tokenpass',
'streamingOptions',
'retentionEnabled',
'retentionMaxAge',
@@ -195,18 +193,6 @@ const settingSavers = {
Team.update(user._id, room.teamId, { type, updateRoom: false });
}
},
- tokenpass({ value, rid }) {
- check(value, {
- require: String,
- tokens: [
- {
- token: String,
- balance: String,
- },
- ],
- });
- saveRoomTokenpass(rid, value);
- },
streamingOptions({ value, rid }) {
saveStreamingOptions(rid, value);
},
diff --git a/apps/meteor/app/chatpal-search/client/template/result.js b/apps/meteor/app/chatpal-search/client/template/result.js
index b96df425c3b4..17a91ae4f6b1 100644
--- a/apps/meteor/app/chatpal-search/client/template/result.js
+++ b/apps/meteor/app/chatpal-search/client/template/result.js
@@ -3,7 +3,7 @@ import { Template } from 'meteor/templating';
import { TAPi18n } from 'meteor/rocketchat:tap-i18n';
import { getURL } from '../../../utils';
-import { Subscriptions } from '../../../models';
+import { Subscriptions } from '../../../models/client';
import { getUserAvatarURL as getAvatarUrl } from '../../../utils/lib/getUserAvatarURL';
import { formatTime } from '../../../../client/lib/utils/formatTime';
import { formatDate } from '../../../../client/lib/utils/formatDate';
diff --git a/apps/meteor/app/chatpal-search/server/provider/index.js b/apps/meteor/app/chatpal-search/server/provider/index.js
index 77f88dcea5f1..77eaa2725596 100644
--- a/apps/meteor/app/chatpal-search/server/provider/index.js
+++ b/apps/meteor/app/chatpal-search/server/provider/index.js
@@ -3,7 +3,7 @@ import { HTTP } from 'meteor/http';
import { Random } from 'meteor/random';
import ChatpalLogger from '../utils/logger';
-import { Rooms, Messages } from '../../../models';
+import { Rooms, Messages } from '../../../models/server';
/**
* Enables HTTP functions on Chatpal Backend
diff --git a/apps/meteor/app/chatpal-search/server/provider/provider.js b/apps/meteor/app/chatpal-search/server/provider/provider.js
index 51670a762c44..b8705cf29be3 100644
--- a/apps/meteor/app/chatpal-search/server/provider/provider.js
+++ b/apps/meteor/app/chatpal-search/server/provider/provider.js
@@ -2,7 +2,7 @@ import { Meteor } from 'meteor/meteor';
import { searchProviderService, SearchProvider } from '../../../search/server';
import ChatpalLogger from '../utils/logger';
-import { Subscriptions, Rooms } from '../../../models';
+import { Subscriptions, Rooms } from '../../../models/server';
import { baseUrl } from '../utils/settings';
import Index from './index';
diff --git a/apps/meteor/app/cloud/server/functions/buildRegistrationData.ts b/apps/meteor/app/cloud/server/functions/buildRegistrationData.ts
index 2b57931ec5cb..712795441ece 100644
--- a/apps/meteor/app/cloud/server/functions/buildRegistrationData.ts
+++ b/apps/meteor/app/cloud/server/functions/buildRegistrationData.ts
@@ -1,8 +1,8 @@
import { SettingValue } from '@rocket.chat/core-typings';
+import { Statistics } from '@rocket.chat/models';
import { settings } from '../../../settings/server';
import { Users } from '../../../models/server';
-import { Statistics } from '../../../models/server/raw';
import { statistics } from '../../../statistics/server';
import { LICENSE_VERSION } from '../license';
diff --git a/apps/meteor/app/cloud/server/functions/checkUserHasCloudLogin.js b/apps/meteor/app/cloud/server/functions/checkUserHasCloudLogin.js
index 39743627152b..e68ff1570498 100644
--- a/apps/meteor/app/cloud/server/functions/checkUserHasCloudLogin.js
+++ b/apps/meteor/app/cloud/server/functions/checkUserHasCloudLogin.js
@@ -1,5 +1,5 @@
import { retrieveRegistrationStatus } from './retrieveRegistrationStatus';
-import { Users } from '../../../models';
+import { Users } from '../../../models/server';
export function checkUserHasCloudLogin(userId) {
const { connectToCloud, workspaceRegistered } = retrieveRegistrationStatus();
diff --git a/apps/meteor/app/cloud/server/functions/connectWorkspace.js b/apps/meteor/app/cloud/server/functions/connectWorkspace.js
index 7ca7fcbb52f9..9c2a424c3221 100644
--- a/apps/meteor/app/cloud/server/functions/connectWorkspace.js
+++ b/apps/meteor/app/cloud/server/functions/connectWorkspace.js
@@ -2,8 +2,8 @@ import { HTTP } from 'meteor/http';
import { getRedirectUri } from './getRedirectUri';
import { retrieveRegistrationStatus } from './retrieveRegistrationStatus';
-import { Settings } from '../../../models';
-import { settings } from '../../../settings';
+import { Settings } from '../../../models/server';
+import { settings } from '../../../settings/server';
import { saveRegistrationData } from './saveRegistrationData';
import { SystemLogger } from '../../../../server/lib/logger/system';
diff --git a/apps/meteor/app/cloud/server/functions/disconnectWorkspace.js b/apps/meteor/app/cloud/server/functions/disconnectWorkspace.js
index c1e2adda876e..c1ac36729aaa 100644
--- a/apps/meteor/app/cloud/server/functions/disconnectWorkspace.js
+++ b/apps/meteor/app/cloud/server/functions/disconnectWorkspace.js
@@ -1,5 +1,5 @@
import { retrieveRegistrationStatus } from './retrieveRegistrationStatus';
-import { Settings } from '../../../models';
+import { Settings } from '../../../models/server';
export function disconnectWorkspace() {
const { connectToCloud } = retrieveRegistrationStatus();
diff --git a/apps/meteor/app/cloud/server/functions/finishOAuthAuthorization.js b/apps/meteor/app/cloud/server/functions/finishOAuthAuthorization.js
index ec0601204795..e46bfda5cfd9 100644
--- a/apps/meteor/app/cloud/server/functions/finishOAuthAuthorization.js
+++ b/apps/meteor/app/cloud/server/functions/finishOAuthAuthorization.js
@@ -2,8 +2,8 @@ import { Meteor } from 'meteor/meteor';
import { HTTP } from 'meteor/http';
import { getRedirectUri } from './getRedirectUri';
-import { settings } from '../../../settings';
-import { Users } from '../../../models';
+import { settings } from '../../../settings/server';
+import { Users } from '../../../models/server';
import { userScopes } from '../oauthScopes';
import { SystemLogger } from '../../../../server/lib/logger/system';
diff --git a/apps/meteor/app/cloud/server/functions/getConfirmationPoll.ts b/apps/meteor/app/cloud/server/functions/getConfirmationPoll.ts
index 9c4533d2e79d..f83dd650d833 100644
--- a/apps/meteor/app/cloud/server/functions/getConfirmationPoll.ts
+++ b/apps/meteor/app/cloud/server/functions/getConfirmationPoll.ts
@@ -10,7 +10,7 @@ export async function getConfirmationPoll(deviceCode: string): Promise('Register_Server'),
+ workspaceRegistered: !!settings.get('Cloud_Workspace_Client_Id'),
+ workspaceId: settings.get('Cloud_Workspace_Id'),
+ uniqueId: settings.get('uniqueID'),
+ token: '',
+ email: settings.get('Organization_Email'),
+ };
+
+ if (!info.email) {
+ const firstUser = Users.getOldest({ emails: 1 });
+ info.email = firstUser?.emails?.[0]?.address;
+ }
+
+ return info;
+}
diff --git a/apps/meteor/app/cloud/server/functions/saveRegistrationData.js b/apps/meteor/app/cloud/server/functions/saveRegistrationData.js
index 1bec537f8952..f9f5e3c05a9d 100644
--- a/apps/meteor/app/cloud/server/functions/saveRegistrationData.js
+++ b/apps/meteor/app/cloud/server/functions/saveRegistrationData.js
@@ -1,4 +1,5 @@
-import { Settings } from '../../../models/server/raw';
+import { Settings } from '@rocket.chat/models';
+
import { callbacks } from '../../../../lib/callbacks';
export function saveRegistrationData({
diff --git a/apps/meteor/app/cloud/server/functions/startRegisterWorkspace.js b/apps/meteor/app/cloud/server/functions/startRegisterWorkspace.js
index e512469248a0..9633a3835159 100644
--- a/apps/meteor/app/cloud/server/functions/startRegisterWorkspace.js
+++ b/apps/meteor/app/cloud/server/functions/startRegisterWorkspace.js
@@ -3,7 +3,7 @@ import { HTTP } from 'meteor/http';
import { retrieveRegistrationStatus } from './retrieveRegistrationStatus';
import { syncWorkspace } from './syncWorkspace';
import { settings } from '../../../settings/server';
-import { Settings } from '../../../models';
+import { Settings } from '../../../models/server';
import { buildWorkspaceRegistrationData } from './buildRegistrationData';
import { SystemLogger } from '../../../../server/lib/logger/system';
diff --git a/apps/meteor/app/cloud/server/functions/startRegisterWorkspaceSetupWizard.ts b/apps/meteor/app/cloud/server/functions/startRegisterWorkspaceSetupWizard.ts
index d19ba9884916..75e2e3c9c51c 100644
--- a/apps/meteor/app/cloud/server/functions/startRegisterWorkspaceSetupWizard.ts
+++ b/apps/meteor/app/cloud/server/functions/startRegisterWorkspaceSetupWizard.ts
@@ -14,7 +14,7 @@ export async function startRegisterWorkspaceSetupWizard(resend = false, email: s
result = HTTP.post(`${cloudUrl}/api/v2/register/workspace/intent?resent=${resend}`, {
data: regInfo,
});
- } catch (e) {
+ } catch (e: any) {
if (e.response && e.response.data && e.response.data.error) {
SystemLogger.error(`Failed to register with Rocket.Chat Cloud. ErrorCode: ${e.response.data.error}`);
} else {
diff --git a/apps/meteor/app/cloud/server/functions/syncWorkspace.js b/apps/meteor/app/cloud/server/functions/syncWorkspace.js
index 6424c5ea867e..ad8ec96faa49 100644
--- a/apps/meteor/app/cloud/server/functions/syncWorkspace.js
+++ b/apps/meteor/app/cloud/server/functions/syncWorkspace.js
@@ -4,8 +4,8 @@ import { buildWorkspaceRegistrationData } from './buildRegistrationData';
import { retrieveRegistrationStatus } from './retrieveRegistrationStatus';
import { getWorkspaceAccessToken } from './getWorkspaceAccessToken';
import { getWorkspaceLicense } from './getWorkspaceLicense';
-import { Settings } from '../../../models';
-import { settings } from '../../../settings';
+import { Settings } from '../../../models/server';
+import { settings } from '../../../settings/server';
import { getAndCreateNpsSurvey } from '../../../../server/services/nps/getAndCreateNpsSurvey';
import { NPS, Banner } from '../../../../server/sdk';
import { SystemLogger } from '../../../../server/lib/logger/system';
diff --git a/apps/meteor/app/cloud/server/functions/unregisterWorkspace.js b/apps/meteor/app/cloud/server/functions/unregisterWorkspace.js
index f28a54a55153..37895e53e248 100644
--- a/apps/meteor/app/cloud/server/functions/unregisterWorkspace.js
+++ b/apps/meteor/app/cloud/server/functions/unregisterWorkspace.js
@@ -1,5 +1,5 @@
import { retrieveRegistrationStatus } from './retrieveRegistrationStatus';
-import { Settings } from '../../../models';
+import { Settings } from '../../../models/server';
export function unregisterWorkspace() {
const { workspaceRegistered } = retrieveRegistrationStatus();
diff --git a/apps/meteor/app/cloud/server/functions/userLoggedOut.js b/apps/meteor/app/cloud/server/functions/userLoggedOut.js
index 6e08b878eb6f..ce1e6c6ac17b 100644
--- a/apps/meteor/app/cloud/server/functions/userLoggedOut.js
+++ b/apps/meteor/app/cloud/server/functions/userLoggedOut.js
@@ -1,4 +1,4 @@
-import { Users } from '../../../models';
+import { Users } from '../../../models/server';
export function userLoggedOut(userId) {
if (!userId) {
diff --git a/apps/meteor/app/cloud/server/functions/userLogout.js b/apps/meteor/app/cloud/server/functions/userLogout.js
index cada35f809fc..6d2e917f347d 100644
--- a/apps/meteor/app/cloud/server/functions/userLogout.js
+++ b/apps/meteor/app/cloud/server/functions/userLogout.js
@@ -2,8 +2,8 @@ import { HTTP } from 'meteor/http';
import { userLoggedOut } from './userLoggedOut';
import { retrieveRegistrationStatus } from './retrieveRegistrationStatus';
-import { Users } from '../../../models';
-import { settings } from '../../../settings';
+import { Users } from '../../../models/server';
+import { settings } from '../../../settings/server';
import { SystemLogger } from '../../../../server/lib/logger/system';
export function userLogout(userId) {
diff --git a/apps/meteor/app/custom-oauth/client/custom_oauth_client.js b/apps/meteor/app/custom-oauth/client/custom_oauth_client.js
index 772aa25f9006..efb73d898dd2 100644
--- a/apps/meteor/app/custom-oauth/client/custom_oauth_client.js
+++ b/apps/meteor/app/custom-oauth/client/custom_oauth_client.js
@@ -7,7 +7,7 @@ import { ServiceConfiguration } from 'meteor/service-configuration';
import { OAuth } from 'meteor/oauth';
import './swapSessionStorage';
-import { isURL } from '../../utils/lib/isURL';
+import { isURL } from '../../../lib/utils/isURL';
// Request custom OAuth credentials for the user
// @param options {optional}
diff --git a/apps/meteor/app/custom-oauth/server/custom_oauth_server.js b/apps/meteor/app/custom-oauth/server/custom_oauth_server.js
index 3b9844e07547..25c5ef82ed88 100644
--- a/apps/meteor/app/custom-oauth/server/custom_oauth_server.js
+++ b/apps/meteor/app/custom-oauth/server/custom_oauth_server.js
@@ -8,8 +8,8 @@ import _ from 'underscore';
import { normalizers, fromTemplate, renameInvalidProperties } from './transform_helpers';
import { Logger } from '../../logger';
-import { Users } from '../../models';
-import { isURL } from '../../utils/lib/isURL';
+import { Users } from '../../models/server';
+import { isURL } from '../../../lib/utils/isURL';
import { registerAccessTokenService } from '../../lib/server/oauth/oauth';
import { callbacks } from '../../../lib/callbacks';
diff --git a/apps/meteor/app/custom-sounds/client/lib/CustomSounds.js b/apps/meteor/app/custom-sounds/client/lib/CustomSounds.js
index 07c83303cb32..5ab704946619 100644
--- a/apps/meteor/app/custom-sounds/client/lib/CustomSounds.js
+++ b/apps/meteor/app/custom-sounds/client/lib/CustomSounds.js
@@ -39,6 +39,20 @@ class CustomSoundsClass {
extension: 'mp3',
src: getURL('sounds/telephone.mp3'),
});
+ this.add({
+ _id: 'outbound-call-ringing',
+ name: 'Outbound Call Ringing',
+ extension: 'mp3',
+ src: getURL('sounds/outbound-call-ringing.mp3'),
+ });
+ this.add({
+ _id: 'call-ended',
+ name: 'Call Ended',
+ extension: 'mp3',
+ src: getURL('sounds/call-ended.mp3'),
+ });
+ this.add({ _id: 'dialtone', name: 'Dialtone', extension: 'mp3', src: getURL('sounds/dialtone.mp3') });
+ this.add({ _id: 'ringtone', name: 'Ringtone', extension: 'mp3', src: getURL('sounds/ringtone.mp3') });
}
add(sound) {
@@ -108,6 +122,12 @@ class CustomSoundsClass {
audio.currentTime = 0;
}
};
+
+ isPlaying = (sound) => {
+ const audio = document.querySelector(`#${getCustomSoundId(sound)}`);
+
+ return audio && audio.duration > 0 && !audio.paused;
+ };
}
export const CustomSounds = new CustomSoundsClass();
diff --git a/apps/meteor/app/custom-sounds/server/methods/deleteCustomSound.js b/apps/meteor/app/custom-sounds/server/methods/deleteCustomSound.js
index 9f935e1d3df4..1a51820ec6e8 100644
--- a/apps/meteor/app/custom-sounds/server/methods/deleteCustomSound.js
+++ b/apps/meteor/app/custom-sounds/server/methods/deleteCustomSound.js
@@ -1,6 +1,6 @@
import { Meteor } from 'meteor/meteor';
+import { CustomSounds } from '@rocket.chat/models';
-import { CustomSounds } from '../../../models/server/raw';
import { hasPermission } from '../../../authorization/server';
import { api } from '../../../../server/sdk/api';
import { RocketChatFileCustomSoundsInstance } from '../startup/custom-sounds';
diff --git a/apps/meteor/app/custom-sounds/server/methods/insertOrUpdateSound.js b/apps/meteor/app/custom-sounds/server/methods/insertOrUpdateSound.js
index fa36d4165b2d..d5cf8ae377bc 100644
--- a/apps/meteor/app/custom-sounds/server/methods/insertOrUpdateSound.js
+++ b/apps/meteor/app/custom-sounds/server/methods/insertOrUpdateSound.js
@@ -1,9 +1,9 @@
import { Meteor } from 'meteor/meteor';
import s from 'underscore.string';
import { check } from 'meteor/check';
+import { CustomSounds } from '@rocket.chat/models';
import { hasPermission } from '../../../authorization/server';
-import { CustomSounds } from '../../../models/server/raw';
import { api } from '../../../../server/sdk/api';
import { RocketChatFileCustomSoundsInstance } from '../startup/custom-sounds';
diff --git a/apps/meteor/app/custom-sounds/server/methods/listCustomSounds.js b/apps/meteor/app/custom-sounds/server/methods/listCustomSounds.js
index 475da52286be..c22506e42ed1 100644
--- a/apps/meteor/app/custom-sounds/server/methods/listCustomSounds.js
+++ b/apps/meteor/app/custom-sounds/server/methods/listCustomSounds.js
@@ -1,6 +1,5 @@
import { Meteor } from 'meteor/meteor';
-
-import { CustomSounds } from '../../../models/server/raw';
+import { CustomSounds } from '@rocket.chat/models';
Meteor.methods({
async listCustomSounds() {
diff --git a/apps/meteor/app/discussion/client/createDiscussionMessageAction.ts b/apps/meteor/app/discussion/client/createDiscussionMessageAction.ts
index 6d7bac1317e0..0c78294b28ce 100644
--- a/apps/meteor/app/discussion/client/createDiscussionMessageAction.ts
+++ b/apps/meteor/app/discussion/client/createDiscussionMessageAction.ts
@@ -4,7 +4,7 @@ import { Tracker } from 'meteor/tracker';
import { settings } from '../../settings/client';
import { hasPermission } from '../../authorization/client';
import { MessageAction } from '../../ui-utils/client';
-import { messageArgs } from '../../ui-utils/client/lib/messageArgs';
+import { messageArgs } from '../../../client/lib/utils/messageArgs';
import { imperativeModal } from '../../../client/lib/imperativeModal';
import CreateDiscussion from '../../../client/components/CreateDiscussion/CreateDiscussion';
import { roomCoordinator } from '../../../client/lib/rooms/roomCoordinator';
diff --git a/apps/meteor/app/discussion/client/tabBar.ts b/apps/meteor/app/discussion/client/tabBar.ts
index fc5f0934cfd5..a9bece626e54 100644
--- a/apps/meteor/app/discussion/client/tabBar.ts
+++ b/apps/meteor/app/discussion/client/tabBar.ts
@@ -1,16 +1,18 @@
import { useMemo, lazy } from 'react';
import { useSetting } from '@rocket.chat/ui-contexts';
+import { isRoomFederated } from '@rocket.chat/core-typings';
import { addAction } from '../../../client/views/room/lib/Toolbox';
const template = lazy(() => import('../../../client/views/room/contextualBar/Discussions'));
-addAction('discussions', ({ room: { prid } }) => {
+addAction('discussions', ({ room }) => {
const discussionEnabled = useSetting('Discussion_enabled');
+ const federated = isRoomFederated(room);
return useMemo(
() =>
- discussionEnabled && !prid
+ discussionEnabled && !room.prid
? {
groups: ['channel', 'group', 'direct', 'direct_multiple', 'team'],
id: 'discussions',
@@ -18,9 +20,13 @@ addAction('discussions', ({ room: { prid } }) => {
icon: 'discussion',
template,
full: true,
+ ...(federated && {
+ 'disabled': true,
+ 'data-tooltip': 'Discussions_unavailable_for_federation',
+ }),
order: 3,
}
: null,
- [discussionEnabled, prid],
+ [discussionEnabled, room.prid, federated],
);
});
diff --git a/apps/meteor/app/discussion/server/permissions.ts b/apps/meteor/app/discussion/server/permissions.ts
index 260b2da71035..fcd2412a77ea 100644
--- a/apps/meteor/app/discussion/server/permissions.ts
+++ b/apps/meteor/app/discussion/server/permissions.ts
@@ -1,6 +1,5 @@
import { Meteor } from 'meteor/meteor';
-
-import { Permissions } from '../../models/server/raw';
+import { Permissions } from '@rocket.chat/models';
Meteor.startup(() => {
// Add permissions for discussion
diff --git a/apps/meteor/app/e2e/client/rocketchat.e2e.room.js b/apps/meteor/app/e2e/client/rocketchat.e2e.room.js
index 223644f56796..fea71e381182 100644
--- a/apps/meteor/app/e2e/client/rocketchat.e2e.room.js
+++ b/apps/meteor/app/e2e/client/rocketchat.e2e.room.js
@@ -3,7 +3,6 @@ import { Base64 } from 'meteor/base64';
import { EJSON } from 'meteor/ejson';
import { Random } from 'meteor/random';
import { Session } from 'meteor/session';
-import { TimeSync } from 'meteor/mizzao:timesync';
import { Emitter } from '@rocket.chat/emitter';
import { e2e } from './rocketchat.e2e';
@@ -395,12 +394,7 @@ export class E2ERoom extends Emitter {
// Helper function for encryption of messages
encrypt(message) {
- let ts;
- if (isNaN(TimeSync.serverOffset())) {
- ts = new Date();
- } else {
- ts = new Date(Date.now() + TimeSync.serverOffset());
- }
+ const ts = new Date();
const data = new TextEncoder('UTF-8').encode(
EJSON.stringify({
diff --git a/apps/meteor/app/e2e/server/methods/fetchMyKeys.js b/apps/meteor/app/e2e/server/methods/fetchMyKeys.js
index f50e8578f815..744cb9cd9c62 100644
--- a/apps/meteor/app/e2e/server/methods/fetchMyKeys.js
+++ b/apps/meteor/app/e2e/server/methods/fetchMyKeys.js
@@ -1,6 +1,6 @@
import { Meteor } from 'meteor/meteor';
-import { Users } from '../../../models';
+import { Users } from '../../../models/server';
Meteor.methods({
'e2e.fetchMyKeys'() {
diff --git a/apps/meteor/app/e2e/server/methods/setUserPublicAndPrivateKeys.js b/apps/meteor/app/e2e/server/methods/setUserPublicAndPrivateKeys.js
index ee8727794f0b..91c6f6b0fc7f 100644
--- a/apps/meteor/app/e2e/server/methods/setUserPublicAndPrivateKeys.js
+++ b/apps/meteor/app/e2e/server/methods/setUserPublicAndPrivateKeys.js
@@ -1,6 +1,6 @@
import { Meteor } from 'meteor/meteor';
-import { Users } from '../../../models';
+import { Users } from '../../../models/server';
Meteor.methods({
'e2e.setUserPublicAndPrivateKeys'({ public_key, private_key }) {
diff --git a/apps/meteor/app/e2e/server/methods/updateGroupKey.js b/apps/meteor/app/e2e/server/methods/updateGroupKey.js
index e7276671c872..9aae29003ddf 100644
--- a/apps/meteor/app/e2e/server/methods/updateGroupKey.js
+++ b/apps/meteor/app/e2e/server/methods/updateGroupKey.js
@@ -1,6 +1,6 @@
import { Meteor } from 'meteor/meteor';
-import { Subscriptions } from '../../../models';
+import { Subscriptions } from '../../../models/server';
Meteor.methods({
'e2e.updateGroupKey'(rid, uid, key) {
diff --git a/apps/meteor/app/emoji-custom/client/lib/emojiCustom.js b/apps/meteor/app/emoji-custom/client/lib/emojiCustom.js
index 46cb53fcbf56..a3df7135cc5f 100644
--- a/apps/meteor/app/emoji-custom/client/lib/emojiCustom.js
+++ b/apps/meteor/app/emoji-custom/client/lib/emojiCustom.js
@@ -184,7 +184,7 @@ Meteor.startup(() =>
try {
const {
emojis: { update: emojis },
- } = await APIClient.v1.get('emoji-custom.list');
+ } = await APIClient.get('/v1/emoji-custom.list');
emoji.packages.emojiCustom.emojisByCategory = { rocket: [] };
for (const currentEmoji of emojis) {
diff --git a/apps/meteor/app/emoji-custom/server/methods/deleteEmojiCustom.js b/apps/meteor/app/emoji-custom/server/methods/deleteEmojiCustom.js
index eceb0d933c31..4d26d9ca065a 100644
--- a/apps/meteor/app/emoji-custom/server/methods/deleteEmojiCustom.js
+++ b/apps/meteor/app/emoji-custom/server/methods/deleteEmojiCustom.js
@@ -1,8 +1,8 @@
import { Meteor } from 'meteor/meteor';
+import { EmojiCustom } from '@rocket.chat/models';
import { api } from '../../../../server/sdk/api';
import { hasPermission } from '../../../authorization';
-import { EmojiCustom } from '../../../models/server/raw';
import { RocketChatFileEmojiCustomInstance } from '../startup/emoji-custom';
Meteor.methods({
diff --git a/apps/meteor/app/emoji-custom/server/methods/insertOrUpdateEmoji.js b/apps/meteor/app/emoji-custom/server/methods/insertOrUpdateEmoji.js
index b26953b1d266..96b95f3fbc6f 100644
--- a/apps/meteor/app/emoji-custom/server/methods/insertOrUpdateEmoji.js
+++ b/apps/meteor/app/emoji-custom/server/methods/insertOrUpdateEmoji.js
@@ -2,9 +2,9 @@ import { Meteor } from 'meteor/meteor';
import _ from 'underscore';
import s from 'underscore.string';
import limax from 'limax';
+import { EmojiCustom } from '@rocket.chat/models';
import { hasPermission } from '../../../authorization';
-import { EmojiCustom } from '../../../models/server/raw';
import { RocketChatFileEmojiCustomInstance } from '../startup/emoji-custom';
import { api } from '../../../../server/sdk/api';
diff --git a/apps/meteor/app/emoji-custom/server/methods/listEmojiCustom.js b/apps/meteor/app/emoji-custom/server/methods/listEmojiCustom.js
index d66aeee1a6ad..a4fd124abe91 100644
--- a/apps/meteor/app/emoji-custom/server/methods/listEmojiCustom.js
+++ b/apps/meteor/app/emoji-custom/server/methods/listEmojiCustom.js
@@ -1,6 +1,5 @@
import { Meteor } from 'meteor/meteor';
-
-import { EmojiCustom } from '../../../models/server/raw';
+import { EmojiCustom } from '@rocket.chat/models';
Meteor.methods({
async listEmojiCustom(options = {}) {
diff --git a/apps/meteor/app/emoji/client/emojiPicker.js b/apps/meteor/app/emoji/client/emojiPicker.js
index 393124a3fbef..671698280cc1 100644
--- a/apps/meteor/app/emoji/client/emojiPicker.js
+++ b/apps/meteor/app/emoji/client/emojiPicker.js
@@ -5,7 +5,6 @@ import { FlowRouter } from 'meteor/kadira:flow-router';
import { Template } from 'meteor/templating';
import { escapeRegExp } from '@rocket.chat/string-helpers';
-import '../../theme/client/imports/components/emojiPicker.css';
import { t } from '../../utils/client';
import { EmojiPicker } from './lib/EmojiPicker';
import { emoji } from '../lib/rocketchat';
@@ -152,7 +151,7 @@ Template.emojiPicker.events({
'click .add-custom'(event) {
event.stopPropagation();
event.preventDefault();
- FlowRouter.go('/admin/emoji-custom');
+ FlowRouter.go('/admin/emoji-custom/new');
EmojiPicker.close();
},
'click .category-link'(event) {
diff --git a/apps/meteor/app/error-handler/server/lib/RocketChat.ErrorHandler.js b/apps/meteor/app/error-handler/server/lib/RocketChat.ErrorHandler.js
index ac0f31e29679..73b7b475f892 100644
--- a/apps/meteor/app/error-handler/server/lib/RocketChat.ErrorHandler.js
+++ b/apps/meteor/app/error-handler/server/lib/RocketChat.ErrorHandler.js
@@ -1,7 +1,7 @@
import { Meteor } from 'meteor/meteor';
import { settings } from '../../../settings/server';
-import { Users, Rooms } from '../../../models';
+import { Users, Rooms, Settings } from '../../../models/server';
import { sendMessage } from '../../../lib';
class ErrorHandler {
@@ -33,6 +33,7 @@ class ErrorHandler {
process.on(
'uncaughtException',
Meteor.bindEnvironment((error) => {
+ Settings.incrementValueById('Uncaught_Exceptions_Count');
if (!this.reporting) {
return;
}
diff --git a/apps/meteor/app/favico/client/favico.js b/apps/meteor/app/favico/client/favico.js
deleted file mode 100644
index b85f9b4a4ee1..000000000000
--- a/apps/meteor/app/favico/client/favico.js
+++ /dev/null
@@ -1,844 +0,0 @@
-/**
- * @license MIT
- * @fileOverview Favico animations
- * @author Miroslav Magda, http://blog.ejci.net
- * @version 0.3.10
- */
-
-/**
- * Create new favico instance
- * @param {Object} Options
- * @return {Object} Favico object
- * @example
- * var favico = new Favico({
- * bgColor : '#d00',
- * textColor : '#fff',
- * fontFamily : 'sans-serif',
- * fontStyle : 'bold',
- * position : 'down',
- * type : 'circle',
- * animation : 'slide',
- * dataUrl: function(url){},
- * win: top
- * });
- */
-/* eslint-disable */
-
- export const Favico = (function(opt) {
- 'use strict';
- opt = (opt) ? opt : {};
- var _def = {
- bgColor: '#d00',
- textColor: '#fff',
- fontFamily: 'sans-serif', //Arial,Verdana,Times New Roman,serif,sans-serif,...
- fontStyle: 'bold', //normal,italic,oblique,bold,bolder,lighter,100,200,300,400,500,600,700,800,900
- type: 'circle',
- position: 'down', // down, up, left, leftup (upleft)
- animation: 'slide',
- elementId: false,
- dataUrl: false,
- win: window
- };
- var _opt, _orig, _h, _w, _canvas, _context, _img, _ready, _lastBadge, _running, _readyCb, _stop, _browser, _animTimeout, _drawTimeout, _doc;
-
- _browser = {};
- _browser.ff = typeof InstallTrigger !== 'undefined';
- _browser.chrome = !!window.chrome;
- _browser.opera = !!window.opera || navigator.userAgent.indexOf('Opera') >= 0;
- _browser.ie = /*@cc_on!@*/ false;
- _browser.safari = Object.prototype.toString.call(window.HTMLElement).indexOf('Constructor') > 0;
- _browser.supported = (_browser.chrome || _browser.ff || _browser.opera);
-
- var _queue = [];
- _readyCb = function() {};
- _ready = _stop = false;
- /**
- * Initialize favico
- */
- var init = function() {
- //merge initial options
- _opt = merge(_def, opt);
- _opt.bgColor = hexToRgb(_opt.bgColor);
- _opt.textColor = hexToRgb(_opt.textColor);
- _opt.position = _opt.position.toLowerCase();
- _opt.animation = (animation.types['' + _opt.animation]) ? _opt.animation : _def.animation;
-
- _doc = _opt.win.document;
-
- var isUp = _opt.position.indexOf('up') > -1;
- var isLeft = _opt.position.indexOf('left') > -1;
-
- //transform the animations
- if (isUp || isLeft) {
- for (var a in animation.types) {
- for (var i = 0; i < animation.types[a].length; i++) {
- var step = animation.types[a][i];
-
- if (isUp) {
- if (step.y < 0.6) {
- step.y = step.y - 0.4;
- } else {
- step.y = step.y - 2 * step.y + (1 - step.w);
- }
- }
-
- if (isLeft) {
- if (step.x < 0.6) {
- step.x = step.x - 0.4;
- } else {
- step.x = step.x - 2 * step.x + (1 - step.h);
- }
- }
-
- animation.types[a][i] = step;
- }
- }
- }
- _opt.type = (type['' + _opt.type]) ? _opt.type : _def.type;
-
- _orig = link.getIcons();
- //create temp canvas
- _canvas = document.createElement('canvas');
- //create temp image
- _img = document.createElement('img');
- var lastIcon = _orig[_orig.length - 1];
- if (lastIcon.hasAttribute('href')) {
- _img.setAttribute('crossOrigin', 'anonymous');
- //get width/height
- _img.onload = function() {
- _h = (_img.height > 0) ? _img.height : 32;
- _w = (_img.width > 0) ? _img.width : 32;
- _canvas.height = _h;
- _canvas.width = _w;
- _context = _canvas.getContext('2d');
- icon.ready();
- };
- _img.setAttribute('src', lastIcon.getAttribute('href'));
- } else {
- _img.onload = function() {
- _h = 32;
- _w = 32;
- _img.height = _h;
- _img.width = _w;
- _canvas.height = _h;
- _canvas.width = _w;
- _context = _canvas.getContext('2d');
- icon.ready();
- };
- _img.setAttribute('src', '');
- }
-
- };
- /**
- * Icon namespace
- */
- var icon = {};
- /**
- * Icon is ready (reset icon) and start animation (if ther is any)
- */
- icon.ready = function() {
- _ready = true;
- icon.reset();
- _readyCb();
- };
- /**
- * Reset icon to default state
- */
- icon.reset = function() {
- //reset
- if (!_ready) {
- return;
- }
- _queue = [];
- _lastBadge = false;
- _running = false;
- _context.clearRect(0, 0, _w, _h);
- _context.drawImage(_img, 0, 0, _w, _h);
- //_stop=true;
- link.setIcon(_canvas);
- //webcam('stop');
- //video('stop');
- window.clearTimeout(_animTimeout);
- window.clearTimeout(_drawTimeout);
- };
- /**
- * Start animation
- */
- icon.start = function() {
- if (!_ready || _running) {
- return;
- }
- var finished = function() {
- _lastBadge = _queue[0];
- _running = false;
- if (_queue.length > 0) {
- _queue.shift();
- icon.start();
- }
- };
- if (_queue.length > 0) {
- _running = true;
- var run = function() {
- // apply options for this animation
- ['type', 'animation', 'bgColor', 'textColor', 'fontFamily', 'fontStyle'].forEach(function(a) {
- if (a in _queue[0].options) {
- _opt[a] = _queue[0].options[a];
- }
- });
- animation.run(_queue[0].options, function() {
- finished();
- }, false);
- };
- if (_lastBadge) {
- animation.run(_lastBadge.options, function() {
- run();
- }, true);
- } else {
- run();
- }
- }
- };
-
- /**
- * Badge types
- */
- var type = {};
- var options = function(opt) {
- opt.n = ((typeof opt.n) === 'number') ? Math.abs(opt.n | 0) : opt.n;
- opt.x = _w * opt.x;
- opt.y = _h * opt.y;
- opt.w = _w * opt.w;
- opt.h = _h * opt.h;
- opt.len = ('' + opt.n).length;
- return opt;
- };
- /**
- * Generate circle
- * @param {Object} opt Badge options
- */
- type.circle = function(opt) {
- opt = options(opt);
- var more = false;
- if (opt.len === 2) {
- opt.x = opt.x - opt.w * 0.4;
- opt.w = opt.w * 1.4;
- more = true;
- } else if (opt.len >= 3) {
- opt.x = opt.x - opt.w * 0.65;
- opt.w = opt.w * 1.65;
- more = true;
- }
- _context.clearRect(0, 0, _w, _h);
- _context.drawImage(_img, 0, 0, _w, _h);
- _context.beginPath();
- _context.font = _opt.fontStyle + ' ' + Math.floor(opt.h * (opt.n > 99 ? 0.85 : 1)) + 'px ' + _opt.fontFamily;
- _context.textAlign = 'center';
- if (more) {
- _context.moveTo(opt.x + opt.w / 2, opt.y);
- _context.lineTo(opt.x + opt.w - opt.h / 2, opt.y);
- _context.quadraticCurveTo(opt.x + opt.w, opt.y, opt.x + opt.w, opt.y + opt.h / 2);
- _context.lineTo(opt.x + opt.w, opt.y + opt.h - opt.h / 2);
- _context.quadraticCurveTo(opt.x + opt.w, opt.y + opt.h, opt.x + opt.w - opt.h / 2, opt.y + opt.h);
- _context.lineTo(opt.x + opt.h / 2, opt.y + opt.h);
- _context.quadraticCurveTo(opt.x, opt.y + opt.h, opt.x, opt.y + opt.h - opt.h / 2);
- _context.lineTo(opt.x, opt.y + opt.h / 2);
- _context.quadraticCurveTo(opt.x, opt.y, opt.x + opt.h / 2, opt.y);
- } else {
- _context.arc(opt.x + opt.w / 2, opt.y + opt.h / 2, opt.h / 2, 0, 2 * Math.PI);
- }
- _context.fillStyle = 'rgba(' + _opt.bgColor.r + ',' + _opt.bgColor.g + ',' + _opt.bgColor.b + ',' + opt.o + ')';
- _context.fill();
- _context.closePath();
- _context.beginPath();
- _context.stroke();
- _context.fillStyle = 'rgba(' + _opt.textColor.r + ',' + _opt.textColor.g + ',' + _opt.textColor.b + ',' + opt.o + ')';
- //_context.fillText((more) ? '9+' : opt.n, Math.floor(opt.x + opt.w / 2), Math.floor(opt.y + opt.h - opt.h * 0.15));
- if ((typeof opt.n) === 'number' && opt.n > 999) {
- _context.fillText(((opt.n > 9999) ? 9 : Math.floor(opt.n / 1000)) + 'k+', Math.floor(opt.x + opt.w / 2), Math.floor(opt.y + opt.h - opt.h * 0.2));
- } else {
- _context.fillText(opt.n, Math.floor(opt.x + opt.w / 2), Math.floor(opt.y + opt.h - opt.h * 0.15));
- }
- _context.closePath();
- };
- /**
- * Generate rectangle
- * @param {Object} opt Badge options
- */
- type.rectangle = function(opt) {
- opt = options(opt);
- var more = false;
- if (opt.len === 2) {
- opt.x = opt.x - opt.w * 0.4;
- opt.w = opt.w * 1.4;
- more = true;
- } else if (opt.len >= 3) {
- opt.x = opt.x - opt.w * 0.65;
- opt.w = opt.w * 1.65;
- more = true;
- }
- _context.clearRect(0, 0, _w, _h);
- _context.drawImage(_img, 0, 0, _w, _h);
- _context.beginPath();
- _context.font = _opt.fontStyle + ' ' + Math.floor(opt.h * (opt.n > 99 ? 0.9 : 1)) + 'px ' + _opt.fontFamily;
- _context.textAlign = 'center';
- _context.fillStyle = 'rgba(' + _opt.bgColor.r + ',' + _opt.bgColor.g + ',' + _opt.bgColor.b + ',' + opt.o + ')';
- _context.fillRect(opt.x, opt.y, opt.w, opt.h);
- _context.fillStyle = 'rgba(' + _opt.textColor.r + ',' + _opt.textColor.g + ',' + _opt.textColor.b + ',' + opt.o + ')';
- //_context.fillText((more) ? '9+' : opt.n, Math.floor(opt.x + opt.w / 2), Math.floor(opt.y + opt.h - opt.h * 0.15));
- if ((typeof opt.n) === 'number' && opt.n > 999) {
- _context.fillText(((opt.n > 9999) ? 9 : Math.floor(opt.n / 1000)) + 'k+', Math.floor(opt.x + opt.w / 2), Math.floor(opt.y + opt.h - opt.h * 0.2));
- } else {
- _context.fillText(opt.n, Math.floor(opt.x + opt.w / 2), Math.floor(opt.y + opt.h - opt.h * 0.15));
- }
- _context.closePath();
- };
-
- /**
- * Set badge
- */
- var badge = function(number, opts) {
- opts = ((typeof opts) === 'string' ? {
- animation: opts
- } : opts) || {};
- _readyCb = function() {
- try {
- if (typeof(number) === 'number' ? (number > 0) : (number !== '')) {
- var q = {
- type: 'badge',
- options: {
- n: number
- }
- };
- if ('animation' in opts && animation.types['' + opts.animation]) {
- q.options.animation = '' + opts.animation;
- }
- if ('type' in opts && type['' + opts.type]) {
- q.options.type = '' + opts.type;
- }
- ['bgColor', 'textColor'].forEach(function(o) {
- if (o in opts) {
- q.options[o] = hexToRgb(opts[o]);
- }
- });
- ['fontStyle', 'fontFamily'].forEach(function(o) {
- if (o in opts) {
- q.options[o] = opts[o];
- }
- });
- _queue.push(q);
- if (_queue.length > 100) {
- throw new Error('Too many badges requests in queue.');
- }
- icon.start();
- } else {
- icon.reset();
- }
- } catch (e) {
- throw new Error('Error setting badge. Message: ' + e.message);
- }
- };
- if (_ready) {
- _readyCb();
- }
- };
-
- /**
- * Set image as icon
- */
- var image = function(imageElement) {
- _readyCb = function() {
- try {
- var w = imageElement.width;
- var h = imageElement.height;
- var newImg = document.createElement('img');
- var ratio = (w / _w < h / _h) ? (w / _w) : (h / _h);
- newImg.setAttribute('crossOrigin', 'anonymous');
- newImg.onload = function() {
- _context.clearRect(0, 0, _w, _h);
- _context.drawImage(newImg, 0, 0, _w, _h);
- link.setIcon(_canvas);
- };
- newImg.setAttribute('src', imageElement.getAttribute('src'));
- newImg.height = (h / ratio);
- newImg.width = (w / ratio);
- } catch (e) {
- throw new Error('Error setting image. Message: ' + e.message);
- }
- };
- if (_ready) {
- _readyCb();
- }
- };
- /**
- * Set video as icon
- */
- var video = function(videoElement) {
- _readyCb = function() {
- try {
- if (videoElement === 'stop') {
- _stop = true;
- icon.reset();
- _stop = false;
- return;
- }
- //var w = videoElement.width;
- //var h = videoElement.height;
- //var ratio = (w / _w < h / _h) ? (w / _w) : (h / _h);
- videoElement.addEventListener('play', function() {
- drawVideo(this);
- }, false);
-
- } catch (e) {
- throw new Error('Error setting video. Message: ' + e.message);
- }
- };
- if (_ready) {
- _readyCb();
- }
- };
- /**
- * Set video as icon
- */
- var webcam = function(action) {
- //UR
- if (!window.URL || !window.URL.createObjectURL) {
- window.URL = window.URL || {};
- window.URL.createObjectURL = function(obj) {
- return obj;
- };
- }
- if (_browser.supported) {
- var newVideo = false;
- navigator.getUserMedia = navigator.getUserMedia || navigator.oGetUserMedia || navigator.msGetUserMedia || navigator.mozGetUserMedia || navigator.webkitGetUserMedia;
- _readyCb = function() {
- try {
- if (action === 'stop') {
- _stop = true;
- icon.reset();
- _stop = false;
- return;
- }
- newVideo = document.createElement('video');
- newVideo.width = _w;
- newVideo.height = _h;
- navigator.getUserMedia({
- video: true,
- audio: false
- }, function(stream) {
- newVideo.src = URL.createObjectURL(stream);
- newVideo.play();
- drawVideo(newVideo);
- }, function() {});
- } catch (e) {
- throw new Error('Error setting webcam. Message: ' + e.message);
- }
- };
- if (_ready) {
- _readyCb();
- }
- }
-
- };
-
- /**
- * Draw video to context and repeat :)
- */
- function drawVideo(video) {
- if (video.paused || video.ended || _stop) {
- return false;
- }
- //nasty hack for FF webcam (Thanks to Julian Ćwirko, kontakt@redsunmedia.pl)
- try {
- _context.clearRect(0, 0, _w, _h);
- _context.drawImage(video, 0, 0, _w, _h);
- } catch (e) {
-
- }
- _drawTimeout = setTimeout(function() {
- drawVideo(video);
- }, animation.duration);
- link.setIcon(_canvas);
- }
-
- var link = {};
- /**
- * Get icons from HEAD tag or create a new element
- */
- link.getIcons = function() {
- var elms = [];
- //get link element
- var getLinks = function() {
- var icons = [];
- var links = _doc.getElementsByTagName('head')[0].getElementsByTagName('link');
- for (var i = 0; i < links.length; i++) {
- if ((/(^|\s)icon(\s|$)/i).test(links[i].getAttribute('rel'))) {
- icons.push(links[i]);
- }
- }
- return icons;
- };
- if (_opt.element) {
- elms = [_opt.element];
- } else if (_opt.elementId) {
- //if img element identified by elementId
- elms = [_doc.getElementById(_opt.elementId)];
- elms[0].setAttribute('href', elms[0].getAttribute('src'));
- } else {
- //if link element
- elms = getLinks();
- if (elms.length === 0) {
- elms = [_doc.createElement('link')];
- elms[0].setAttribute('rel', 'icon');
- _doc.getElementsByTagName('head')[0].appendChild(elms[0]);
- }
- }
- elms.forEach(function(item) {
- item.setAttribute('type', 'image/png');
- });
- return elms;
- };
- link.setIcon = function(canvas) {
- var url = canvas.toDataURL('image/png');
- if (_opt.dataUrl) {
- //if using custom exporter
- _opt.dataUrl(url);
- }
- if (_opt.element) {
- _opt.element.setAttribute('href', url);
- _opt.element.setAttribute('src', url);
- } else if (_opt.elementId) {
- //if is attached to element (image)
- var elm = _doc.getElementById(_opt.elementId);
- elm.setAttribute('href', url);
- elm.setAttribute('src', url);
- } else {
- //if is attached to fav icon
- if (_browser.ff || _browser.opera) {
- //for FF we need to "recreate" element, atach to dom and remove old
- //var originalType = _orig.getAttribute('rel');
- var old = _orig[_orig.length - 1];
- var newIcon = _doc.createElement('link');
- _orig = [newIcon];
- //_orig.setAttribute('rel', originalType);
- if (_browser.opera) {
- newIcon.setAttribute('rel', 'icon');
- }
- newIcon.setAttribute('rel', 'icon');
- newIcon.setAttribute('type', 'image/png');
- _doc.getElementsByTagName('head')[0].appendChild(newIcon);
- newIcon.setAttribute('href', url);
- if (old.parentNode) {
- old.parentNode.removeChild(old);
- }
- } else {
- _orig.forEach(function(icon) {
- icon.setAttribute('href', url);
- });
- }
- }
- };
-
- //http://stackoverflow.com/questions/5623838/rgb-to-hex-and-hex-to-rgb#answer-5624139
- //HEX to RGB convertor
- function hexToRgb(hex) {
- var shorthandRegex = /^#?([a-f\d])([a-f\d])([a-f\d])$/i;
- hex = hex.replace(shorthandRegex, function(m, r, g, b) {
- return r + r + g + g + b + b;
- });
- var result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex);
- return result ? {
- r: parseInt(result[1], 16),
- g: parseInt(result[2], 16),
- b: parseInt(result[3], 16)
- } : false;
- }
-
- /**
- * Merge options
- */
- function merge(def, opt) {
- var mergedOpt = {};
- var attrname;
- for (attrname in def) {
- mergedOpt[attrname] = def[attrname];
- }
- for (attrname in opt) {
- mergedOpt[attrname] = opt[attrname];
- }
- return mergedOpt;
- }
-
- /**
- * Cross-browser page visibility shim
- * http://stackoverflow.com/questions/12536562/detect-whether-a-window-is-visible
- */
- function isPageHidden() {
- return _doc.hidden || _doc.msHidden || _doc.webkitHidden || _doc.mozHidden;
- }
-
- /**
- * @namespace animation
- */
- var animation = {};
- /**
- * Animation "frame" duration
- */
- animation.duration = 40;
- /**
- * Animation types (none,fade,pop,slide)
- */
- animation.types = {};
- animation.types.fade = [{
- x: 0.4,
- y: 0.4,
- w: 0.6,
- h: 0.6,
- o: 0.0
- }, {
- x: 0.4,
- y: 0.4,
- w: 0.6,
- h: 0.6,
- o: 0.1
- }, {
- x: 0.4,
- y: 0.4,
- w: 0.6,
- h: 0.6,
- o: 0.2
- }, {
- x: 0.4,
- y: 0.4,
- w: 0.6,
- h: 0.6,
- o: 0.3
- }, {
- x: 0.4,
- y: 0.4,
- w: 0.6,
- h: 0.6,
- o: 0.4
- }, {
- x: 0.4,
- y: 0.4,
- w: 0.6,
- h: 0.6,
- o: 0.5
- }, {
- x: 0.4,
- y: 0.4,
- w: 0.6,
- h: 0.6,
- o: 0.6
- }, {
- x: 0.4,
- y: 0.4,
- w: 0.6,
- h: 0.6,
- o: 0.7
- }, {
- x: 0.4,
- y: 0.4,
- w: 0.6,
- h: 0.6,
- o: 0.8
- }, {
- x: 0.4,
- y: 0.4,
- w: 0.6,
- h: 0.6,
- o: 0.9
- }, {
- x: 0.4,
- y: 0.4,
- w: 0.6,
- h: 0.6,
- o: 1.0
- }];
- animation.types.none = [{
- x: 0.4,
- y: 0.4,
- w: 0.6,
- h: 0.6,
- o: 1
- }];
- animation.types.pop = [{
- x: 1,
- y: 1,
- w: 0,
- h: 0,
- o: 1
- }, {
- x: 0.9,
- y: 0.9,
- w: 0.1,
- h: 0.1,
- o: 1
- }, {
- x: 0.8,
- y: 0.8,
- w: 0.2,
- h: 0.2,
- o: 1
- }, {
- x: 0.7,
- y: 0.7,
- w: 0.3,
- h: 0.3,
- o: 1
- }, {
- x: 0.6,
- y: 0.6,
- w: 0.4,
- h: 0.4,
- o: 1
- }, {
- x: 0.5,
- y: 0.5,
- w: 0.5,
- h: 0.5,
- o: 1
- }, {
- x: 0.4,
- y: 0.4,
- w: 0.6,
- h: 0.6,
- o: 1
- }];
- animation.types.popFade = [{
- x: 0.75,
- y: 0.75,
- w: 0,
- h: 0,
- o: 0
- }, {
- x: 0.65,
- y: 0.65,
- w: 0.1,
- h: 0.1,
- o: 0.2
- }, {
- x: 0.6,
- y: 0.6,
- w: 0.2,
- h: 0.2,
- o: 0.4
- }, {
- x: 0.55,
- y: 0.55,
- w: 0.3,
- h: 0.3,
- o: 0.6
- }, {
- x: 0.50,
- y: 0.50,
- w: 0.4,
- h: 0.4,
- o: 0.8
- }, {
- x: 0.45,
- y: 0.45,
- w: 0.5,
- h: 0.5,
- o: 0.9
- }, {
- x: 0.4,
- y: 0.4,
- w: 0.6,
- h: 0.6,
- o: 1
- }];
- animation.types.slide = [{
- x: 0.4,
- y: 1,
- w: 0.6,
- h: 0.6,
- o: 1
- }, {
- x: 0.4,
- y: 0.9,
- w: 0.6,
- h: 0.6,
- o: 1
- }, {
- x: 0.4,
- y: 0.9,
- w: 0.6,
- h: 0.6,
- o: 1
- }, {
- x: 0.4,
- y: 0.8,
- w: 0.6,
- h: 0.6,
- o: 1
- }, {
- x: 0.4,
- y: 0.7,
- w: 0.6,
- h: 0.6,
- o: 1
- }, {
- x: 0.4,
- y: 0.6,
- w: 0.6,
- h: 0.6,
- o: 1
- }, {
- x: 0.4,
- y: 0.5,
- w: 0.6,
- h: 0.6,
- o: 1
- }, {
- x: 0.4,
- y: 0.4,
- w: 0.6,
- h: 0.6,
- o: 1
- }];
- /**
- * Run animation
- * @param {Object} opt Animation options
- * @param {Object} cb Callabak after all steps are done
- * @param {Object} revert Reverse order? true|false
- * @param {Object} step Optional step number (frame bumber)
- */
- animation.run = function(opt, cb, revert, step) {
- var animationType = animation.types[isPageHidden() ? 'none' : _opt.animation];
- if (revert === true) {
- step = (typeof step !== 'undefined') ? step : animationType.length - 1;
- } else {
- step = (typeof step !== 'undefined') ? step : 0;
- }
- cb = (cb) ? cb : function() {};
- if ((step < animationType.length) && (step >= 0)) {
- type[_opt.type](merge(opt, animationType[step]));
- _animTimeout = setTimeout(function() {
- if (revert) {
- step = step - 1;
- } else {
- step = step + 1;
- }
- animation.run(opt, cb, revert, step);
- }, animation.duration);
-
- link.setIcon(_canvas);
- } else {
- cb();
- return;
- }
- };
- //auto init
- init();
- return {
- badge: badge,
- video: video,
- image: image,
- webcam: webcam,
- reset: icon.reset,
- browser: {
- supported: _browser.supported
- }
- };
- });
diff --git a/apps/meteor/app/favico/client/index.js b/apps/meteor/app/favico/client/index.js
deleted file mode 100644
index 239a252e455c..000000000000
--- a/apps/meteor/app/favico/client/index.js
+++ /dev/null
@@ -1,3 +0,0 @@
-import { Favico } from './favico';
-
-export { Favico };
diff --git a/apps/meteor/app/favico/index.js b/apps/meteor/app/favico/index.js
deleted file mode 100644
index 40a7340d3887..000000000000
--- a/apps/meteor/app/favico/index.js
+++ /dev/null
@@ -1 +0,0 @@
-export * from './client/index';
diff --git a/apps/meteor/app/federation-v2/client/Federation.ts b/apps/meteor/app/federation-v2/client/Federation.ts
new file mode 100644
index 000000000000..552c3a47623b
--- /dev/null
+++ b/apps/meteor/app/federation-v2/client/Federation.ts
@@ -0,0 +1,24 @@
+import { IRoom, IUser, ValueOf } from '@rocket.chat/core-typings';
+import { RoomType } from '@rocket.chat/apps-engine/definition/rooms';
+
+import { RoomMemberActions } from '../../../definition/IRoomTypeConfig';
+
+const allowedActionsInFederatedRooms: ValueOf[] = [
+ RoomMemberActions.REMOVE_USER,
+ RoomMemberActions.INVITE,
+ RoomMemberActions.JOIN,
+ RoomMemberActions.LEAVE,
+];
+
+export const actionAllowed = (room: Partial, action: ValueOf): boolean => {
+ return room.t === RoomType.DIRECT_MESSAGE && action === RoomMemberActions.REMOVE_USER
+ ? false
+ : allowedActionsInFederatedRooms.includes(action);
+};
+
+export const isEditableByTheUser = (user: IUser | undefined, room: IRoom | undefined): boolean => {
+ if (!user || !room) {
+ return false;
+ }
+ return user._id === room.u?._id;
+};
diff --git a/apps/meteor/app/federation-v2/client/index.ts b/apps/meteor/app/federation-v2/client/index.ts
new file mode 100644
index 000000000000..37ec361a570a
--- /dev/null
+++ b/apps/meteor/app/federation-v2/client/index.ts
@@ -0,0 +1 @@
+import './slash-commands';
diff --git a/apps/meteor/app/federation-v2/client/slash-commands/index.ts b/apps/meteor/app/federation-v2/client/slash-commands/index.ts
new file mode 100644
index 000000000000..7e32258f20c4
--- /dev/null
+++ b/apps/meteor/app/federation-v2/client/slash-commands/index.ts
@@ -0,0 +1,20 @@
+import { slashCommands } from '../../../utils/lib/slashCommand';
+
+const callback = undefined;
+const result = undefined;
+const providesPreview = false;
+const previewer = undefined;
+const previewCallback = undefined;
+
+slashCommands.add({
+ command: 'federation',
+ callback,
+ options: {
+ description: 'Federation_slash_commands',
+ params: '#command (dm) #user',
+ },
+ result,
+ providesPreview,
+ previewer,
+ previewCallback,
+});
diff --git a/apps/meteor/app/federation-v2/server/application/RoomServiceReceiver.ts b/apps/meteor/app/federation-v2/server/application/RoomServiceReceiver.ts
new file mode 100644
index 000000000000..4171426bb62b
--- /dev/null
+++ b/apps/meteor/app/federation-v2/server/application/RoomServiceReceiver.ts
@@ -0,0 +1,263 @@
+import { RoomType } from '@rocket.chat/apps-engine/definition/rooms';
+
+import { FederatedRoom } from '../domain/FederatedRoom';
+import { FederatedUser } from '../domain/FederatedUser';
+import { EVENT_ORIGIN, IFederationBridge } from '../domain/IFederationBridge';
+import { RocketChatMessageAdapter } from '../infrastructure/rocket-chat/adapters/Message';
+import { RocketChatRoomAdapter } from '../infrastructure/rocket-chat/adapters/Room';
+import { RocketChatSettingsAdapter } from '../infrastructure/rocket-chat/adapters/Settings';
+import { RocketChatUserAdapter } from '../infrastructure/rocket-chat/adapters/User';
+import {
+ FederationRoomCreateInputDto,
+ FederationRoomChangeMembershipDto,
+ FederationRoomSendInternalMessageDto,
+ FederationRoomChangeJoinRulesDto,
+ FederationRoomChangeNameDto,
+ FederationRoomChangeTopicDto,
+} from './input/RoomReceiverDto';
+
+export class FederationRoomServiceReceiver {
+ constructor(
+ protected rocketRoomAdapter: RocketChatRoomAdapter,
+ protected rocketUserAdapter: RocketChatUserAdapter,
+ protected rocketMessageAdapter: RocketChatMessageAdapter,
+ protected rocketSettingsAdapter: RocketChatSettingsAdapter,
+ protected bridge: IFederationBridge,
+ ) {} // eslint-disable-line no-empty-function
+
+ public async createRoom(roomCreateInput: FederationRoomCreateInputDto): Promise {
+ const {
+ externalRoomId,
+ externalInviterId,
+ normalizedInviterId,
+ externalRoomName,
+ normalizedRoomId,
+ roomType,
+ wasInternallyProgramaticallyCreated = false,
+ } = roomCreateInput;
+
+ if ((await this.rocketRoomAdapter.getFederatedRoomByExternalId(externalRoomId)) || wasInternallyProgramaticallyCreated) {
+ return;
+ }
+
+ if (!(await this.rocketUserAdapter.getFederatedUserByExternalId(externalInviterId))) {
+ const externalUserProfileInformation = await this.bridge.getUserProfileInformation(externalInviterId);
+ const name = externalUserProfileInformation?.displayname || normalizedInviterId;
+ const federatedCreatorUser = FederatedUser.createInstance(externalInviterId, {
+ name,
+ username: normalizedInviterId,
+ existsOnlyOnProxyServer: false,
+ });
+
+ await this.rocketUserAdapter.createFederatedUser(federatedCreatorUser);
+ }
+ const creator = await this.rocketUserAdapter.getFederatedUserByExternalId(externalInviterId);
+ const newFederatedRoom = FederatedRoom.createInstance(
+ externalRoomId,
+ normalizedRoomId,
+ creator as FederatedUser,
+ roomType || RoomType.CHANNEL,
+ externalRoomName,
+ );
+ await this.rocketRoomAdapter.createFederatedRoom(newFederatedRoom);
+ }
+
+ public async changeRoomMembership(roomChangeMembershipInput: FederationRoomChangeMembershipDto): Promise {
+ const {
+ externalRoomId,
+ normalizedInviteeId,
+ normalizedRoomId,
+ normalizedInviterId,
+ externalRoomName,
+ externalInviteeId,
+ externalInviterId,
+ inviteeUsernameOnly,
+ inviterUsernameOnly,
+ eventOrigin,
+ roomType,
+ leave,
+ } = roomChangeMembershipInput;
+ const affectedFederatedRoom = await this.rocketRoomAdapter.getFederatedRoomByExternalId(externalRoomId);
+
+ if (!affectedFederatedRoom && eventOrigin === EVENT_ORIGIN.LOCAL) {
+ throw new Error(`Could not find room with external room id: ${externalRoomId}`);
+ }
+ const isInviterFromTheSameHomeServer = this.bridge.isUserIdFromTheSameHomeserver(
+ externalInviterId,
+ this.rocketSettingsAdapter.getHomeServerDomain(),
+ );
+ const isInviteeFromTheSameHomeServer = this.bridge.isUserIdFromTheSameHomeserver(
+ externalInviteeId,
+ this.rocketSettingsAdapter.getHomeServerDomain(),
+ );
+
+ if (!(await this.rocketUserAdapter.getFederatedUserByExternalId(externalInviterId))) {
+ const externalUserProfileInformation = await this.bridge.getUserProfileInformation(externalInviterId);
+ const name = externalUserProfileInformation?.displayname || normalizedInviterId;
+ const username = isInviterFromTheSameHomeServer ? inviterUsernameOnly : normalizedInviterId;
+ const federatedInviterUser = FederatedUser.createInstance(externalInviterId, {
+ name,
+ username,
+ existsOnlyOnProxyServer: isInviterFromTheSameHomeServer,
+ });
+
+ await this.rocketUserAdapter.createFederatedUser(federatedInviterUser);
+ }
+
+ if (!(await this.rocketUserAdapter.getFederatedUserByExternalId(externalInviteeId))) {
+ const externalUserProfileInformation = await this.bridge.getUserProfileInformation(externalInviteeId);
+ const name = externalUserProfileInformation?.displayname || normalizedInviteeId;
+ const username = isInviteeFromTheSameHomeServer ? inviteeUsernameOnly : normalizedInviteeId;
+ const federatedInviteeUser = FederatedUser.createInstance(externalInviteeId, {
+ name,
+ username,
+ existsOnlyOnProxyServer: isInviteeFromTheSameHomeServer,
+ });
+
+ await this.rocketUserAdapter.createFederatedUser(federatedInviteeUser);
+ }
+
+ const federatedInviteeUser = await this.rocketUserAdapter.getFederatedUserByExternalId(externalInviteeId);
+ const federatedInviterUser = await this.rocketUserAdapter.getFederatedUserByExternalId(externalInviterId);
+ if (!affectedFederatedRoom && eventOrigin === EVENT_ORIGIN.REMOTE) {
+ const members = [federatedInviterUser, federatedInviteeUser] as FederatedUser[];
+ const newFederatedRoom = FederatedRoom.createInstance(
+ externalRoomId,
+ normalizedRoomId,
+ federatedInviterUser as FederatedUser,
+ roomType,
+ externalRoomName,
+ members,
+ );
+
+ await this.rocketRoomAdapter.createFederatedRoom(newFederatedRoom);
+ await this.bridge.joinRoom(externalRoomId, externalInviteeId);
+ }
+ const federatedRoom = affectedFederatedRoom || (await this.rocketRoomAdapter.getFederatedRoomByExternalId(externalRoomId));
+ if (leave) {
+ if (
+ !(await this.rocketRoomAdapter.isUserAlreadyJoined(
+ federatedRoom?.internalReference?._id as string,
+ federatedInviteeUser?.internalReference._id as string,
+ ))
+ ) {
+ return;
+ }
+
+ return this.rocketRoomAdapter.removeUserFromRoom(
+ federatedRoom as FederatedRoom,
+ federatedInviteeUser as FederatedUser,
+ federatedInviterUser as FederatedUser,
+ );
+ }
+ if (affectedFederatedRoom?.isDirectMessage() && eventOrigin === EVENT_ORIGIN.REMOTE) {
+ const membersUsernames = [
+ ...(affectedFederatedRoom.internalReference?.usernames || []),
+ federatedInviteeUser?.internalReference.username as string,
+ ];
+ const newFederatedRoom = FederatedRoom.createInstance(
+ externalRoomId,
+ normalizedRoomId,
+ federatedInviterUser as FederatedUser,
+ RoomType.DIRECT_MESSAGE,
+ externalRoomName,
+ );
+ if (affectedFederatedRoom.internalReference?.usernames?.includes(federatedInviteeUser?.internalReference.username || '')) {
+ return;
+ }
+ await this.rocketRoomAdapter.removeDirectMessageRoom(affectedFederatedRoom);
+ await this.rocketRoomAdapter.createFederatedRoomForDirectMessage(newFederatedRoom, membersUsernames);
+ return;
+ }
+
+ await this.rocketRoomAdapter.addUserToRoom(
+ federatedRoom as FederatedRoom,
+ federatedInviteeUser as FederatedUser,
+ federatedInviterUser as FederatedUser,
+ );
+ }
+
+ public async receiveExternalMessage(roomSendInternalMessageInput: FederationRoomSendInternalMessageDto): Promise {
+ const { externalRoomId, externalSenderId, text } = roomSendInternalMessageInput;
+
+ const federatedRoom = await this.rocketRoomAdapter.getFederatedRoomByExternalId(externalRoomId);
+ if (!federatedRoom) {
+ return;
+ }
+
+ const senderUser = await this.rocketUserAdapter.getFederatedUserByExternalId(externalSenderId);
+ if (!senderUser) {
+ return;
+ }
+
+ await this.rocketMessageAdapter.sendMessage(senderUser, text, federatedRoom);
+ }
+
+ public async changeJoinRules(roomJoinRulesChangeInput: FederationRoomChangeJoinRulesDto): Promise {
+ const { externalRoomId, roomType } = roomJoinRulesChangeInput;
+
+ const federatedRoom = await this.rocketRoomAdapter.getFederatedRoomByExternalId(externalRoomId);
+ if (!federatedRoom) {
+ return;
+ }
+
+ if (federatedRoom.isDirectMessage()) {
+ return;
+ }
+
+ federatedRoom.setRoomType(roomType);
+ await this.rocketRoomAdapter.updateRoomType(federatedRoom);
+ }
+
+ public async changeRoomName(roomChangeNameInput: FederationRoomChangeNameDto): Promise {
+ const { externalRoomId, normalizedRoomName, externalSenderId } = roomChangeNameInput;
+
+ const federatedRoom = await this.rocketRoomAdapter.getFederatedRoomByExternalId(externalRoomId);
+ if (!federatedRoom) {
+ return;
+ }
+
+ if (federatedRoom.isDirectMessage()) {
+ return;
+ }
+
+ if (federatedRoom.internalReference?.name === normalizedRoomName) {
+ return;
+ }
+
+ const federatedUser = await this.rocketUserAdapter.getFederatedUserByExternalId(externalSenderId);
+ if (!federatedUser) {
+ return;
+ }
+
+ federatedRoom.changeRoomName(normalizedRoomName);
+
+ await this.rocketRoomAdapter.updateRoomName(federatedRoom, federatedUser);
+ }
+
+ public async changeRoomTopic(roomChangeTopicInput: FederationRoomChangeTopicDto): Promise {
+ const { externalRoomId, roomTopic, externalSenderId } = roomChangeTopicInput;
+
+ const federatedRoom = await this.rocketRoomAdapter.getFederatedRoomByExternalId(externalRoomId);
+ if (!federatedRoom) {
+ return;
+ }
+
+ if (federatedRoom.internalReference?.topic === roomTopic) {
+ return;
+ }
+
+ if (federatedRoom.isDirectMessage()) {
+ return;
+ }
+
+ const federatedUser = await this.rocketUserAdapter.getFederatedUserByExternalId(externalSenderId);
+ if (!federatedUser) {
+ return;
+ }
+
+ federatedRoom.changeRoomTopic(roomTopic);
+
+ await this.rocketRoomAdapter.updateRoomTopic(federatedRoom, federatedUser);
+ }
+}
diff --git a/apps/meteor/app/federation-v2/server/application/RoomServiceSender.ts b/apps/meteor/app/federation-v2/server/application/RoomServiceSender.ts
new file mode 100644
index 000000000000..8d21f8241648
--- /dev/null
+++ b/apps/meteor/app/federation-v2/server/application/RoomServiceSender.ts
@@ -0,0 +1,198 @@
+import { RoomType } from '@rocket.chat/apps-engine/definition/rooms';
+import { IMessage, IRoom, IUser } from '@rocket.chat/core-typings';
+
+import { FederatedRoom } from '../domain/FederatedRoom';
+import { FederatedUser } from '../domain/FederatedUser';
+import { IFederationBridge } from '../domain/IFederationBridge';
+import { RocketChatRoomAdapter } from '../infrastructure/rocket-chat/adapters/Room';
+import { RocketChatSettingsAdapter } from '../infrastructure/rocket-chat/adapters/Settings';
+import { RocketChatUserAdapter } from '../infrastructure/rocket-chat/adapters/User';
+import {
+ FederationAfterLeaveRoomDto,
+ FederationCreateDMAndInviteUserDto,
+ FederationRoomSendExternalMessageDto,
+} from './input/RoomSenderDto';
+
+export class FederationRoomServiceSender {
+ constructor(
+ protected rocketRoomAdapter: RocketChatRoomAdapter,
+ protected rocketUserAdapter: RocketChatUserAdapter,
+ protected rocketSettingsAdapter: RocketChatSettingsAdapter,
+ protected bridge: IFederationBridge,
+ ) {} // eslint-disable-line no-empty-function
+
+ public async createDirectMessageRoomAndInviteUser(roomCreateDMAndInviteUserInput: FederationCreateDMAndInviteUserDto): Promise {
+ const { normalizedInviteeId, rawInviteeId, internalInviterId, inviteeUsernameOnly } = roomCreateDMAndInviteUserInput;
+
+ if (!(await this.rocketUserAdapter.getFederatedUserByInternalId(internalInviterId))) {
+ const internalUser = (await this.rocketUserAdapter.getInternalUserById(internalInviterId)) as IUser;
+ const externalInviterId = await this.bridge.createUser(
+ internalUser.username as string,
+ internalUser.name as string,
+ this.rocketSettingsAdapter.getHomeServerDomain(),
+ );
+ const federatedInviterUser = FederatedUser.createInstance(externalInviterId, {
+ name: internalUser.name as string,
+ username: internalUser.username as string,
+ existsOnlyOnProxyServer: true,
+ });
+ await this.rocketUserAdapter.createFederatedUser(federatedInviterUser);
+ }
+
+ if (!(await this.rocketUserAdapter.getFederatedUserByInternalUsername(normalizedInviteeId))) {
+ const externalUserProfileInformation = await this.bridge.getUserProfileInformation(rawInviteeId);
+ const name = externalUserProfileInformation?.displayname || normalizedInviteeId;
+ const federatedInviteeUser = FederatedUser.createInstance(rawInviteeId, {
+ name,
+ username: normalizedInviteeId,
+ existsOnlyOnProxyServer: false,
+ });
+
+ await this.rocketUserAdapter.createFederatedUser(federatedInviteeUser);
+ }
+ const federatedInviterUser = (await this.rocketUserAdapter.getFederatedUserByInternalId(internalInviterId)) as FederatedUser;
+ const federatedInviteeUser = (await this.rocketUserAdapter.getFederatedUserByInternalUsername(normalizedInviteeId)) as FederatedUser;
+ const isInviteeFromTheSameHomeServer = this.bridge.isUserIdFromTheSameHomeserver(
+ rawInviteeId,
+ this.rocketSettingsAdapter.getHomeServerDomain(),
+ );
+ const internalRoomId = FederatedRoom.buildRoomIdForDirectMessages(federatedInviterUser, federatedInviteeUser);
+
+ if (!(await this.rocketRoomAdapter.getFederatedRoomByInternalId(internalRoomId))) {
+ const externalRoomId = await this.bridge.createDirectMessageRoom(federatedInviterUser.externalId, [federatedInviteeUser.externalId]);
+ const newFederatedRoom = FederatedRoom.createInstance(
+ externalRoomId,
+ externalRoomId,
+ federatedInviterUser,
+ RoomType.DIRECT_MESSAGE,
+ '',
+ [federatedInviterUser, federatedInviteeUser] as any[],
+ );
+ await this.rocketRoomAdapter.createFederatedRoom(newFederatedRoom);
+ }
+
+ const federatedRoom = (await this.rocketRoomAdapter.getFederatedRoomByInternalId(internalRoomId)) as FederatedRoom;
+ if (isInviteeFromTheSameHomeServer) {
+ await this.bridge.createUser(
+ inviteeUsernameOnly,
+ federatedInviteeUser?.internalReference?.name || normalizedInviteeId,
+ this.rocketSettingsAdapter.getHomeServerDomain(),
+ );
+ await this.bridge.inviteToRoom(federatedRoom.externalId, federatedInviterUser.externalId, federatedInviteeUser.externalId);
+ await this.bridge.joinRoom(federatedRoom.externalId, federatedInviteeUser.externalId);
+ }
+ await this.rocketRoomAdapter.addUserToRoom(federatedRoom, federatedInviteeUser, federatedInviterUser);
+ }
+
+ public async leaveRoom(afterLeaveRoomInput: FederationAfterLeaveRoomDto): Promise {
+ const { internalRoomId, internalUserId, whoRemovedInternalId } = afterLeaveRoomInput;
+
+ const federatedRoom = await this.rocketRoomAdapter.getFederatedRoomByInternalId(internalRoomId);
+ if (!federatedRoom) {
+ return;
+ }
+
+ const federatedUser = await this.rocketUserAdapter.getFederatedUserByInternalId(internalUserId);
+ if (!federatedUser) {
+ return;
+ }
+
+ if (whoRemovedInternalId) {
+ const who = await this.rocketUserAdapter.getFederatedUserByInternalId(whoRemovedInternalId);
+ await this.bridge.kickUserFromRoom(federatedRoom.externalId, federatedUser.externalId, who?.externalId as string);
+ return;
+ }
+
+ await this.bridge.leaveRoom(federatedRoom.externalId, federatedUser.externalId);
+ }
+
+ public async sendMessageFromRocketChat(roomSendExternalMessageInput: FederationRoomSendExternalMessageDto): Promise {
+ const { internalRoomId, internalSenderId, message } = roomSendExternalMessageInput;
+
+ const federatedSender = await this.rocketUserAdapter.getFederatedUserByInternalId(internalSenderId);
+ const federatedRoom = await this.rocketRoomAdapter.getFederatedRoomByInternalId(internalRoomId);
+
+ if (!federatedSender) {
+ throw new Error(`Could not find user id for ${internalSenderId}`);
+ }
+ if (!federatedRoom) {
+ throw new Error(`Could not find room id for ${internalRoomId}`);
+ }
+ await this.bridge.sendMessage(federatedRoom.externalId, federatedSender.externalId, message.msg);
+
+ return message;
+ }
+
+ public async isAFederatedRoom(internalRoomId: string): Promise {
+ if (!internalRoomId) {
+ return false;
+ }
+ const federatedRoom = await this.rocketRoomAdapter.getFederatedRoomByInternalId(internalRoomId);
+
+ return Boolean(federatedRoom?.isFederated());
+ }
+
+ public async canAddThisUserToTheRoom(internalUser: IUser | string, internalRoom: IRoom): Promise {
+ const newUserBeingAdded = typeof internalUser === 'string';
+ if (newUserBeingAdded) {
+ return;
+ }
+
+ if (internalRoom.federated) {
+ return;
+ }
+
+ const user = await this.rocketUserAdapter.getFederatedUserByInternalId((internalUser as IUser)._id);
+ if (user && !user.existsOnlyOnProxyServer) {
+ throw new Error('error-cant-add-federated-users');
+ }
+ }
+
+ public async canAddUsersToTheRoom(internalUser: IUser | string, internalInviter: IUser, internalRoom: IRoom): Promise {
+ if (!internalRoom.federated) {
+ return;
+ }
+ const tryingToAddNewFederatedUser = typeof internalUser === 'string';
+ if (tryingToAddNewFederatedUser) {
+ throw new Error('error-this-is-an-ee-feature');
+ }
+
+ const invitee = await this.rocketUserAdapter.getFederatedUserByInternalId((internalUser as IUser)._id);
+ const inviter = await this.rocketUserAdapter.getFederatedUserByInternalId((internalInviter as IUser)._id);
+ const externalRoom = await this.rocketRoomAdapter.getFederatedRoomByInternalId(internalRoom._id);
+ if (!externalRoom || !inviter) {
+ return;
+ }
+
+ const isARoomFromTheProxyServer = this.bridge.isRoomFromTheSameHomeserver(
+ externalRoom.externalId,
+ this.rocketSettingsAdapter.getHomeServerDomain(),
+ );
+ const isInviterFromTheProxyServer = this.bridge.isUserIdFromTheSameHomeserver(
+ inviter.externalId,
+ this.rocketSettingsAdapter.getHomeServerDomain(),
+ );
+
+ if (!isARoomFromTheProxyServer && !isInviterFromTheProxyServer) {
+ return;
+ }
+ if (invitee && !invitee.existsOnlyOnProxyServer && internalRoom.t !== RoomType.DIRECT_MESSAGE) {
+ throw new Error('error-this-is-an-ee-feature');
+ }
+ }
+
+ public async beforeCreateDirectMessageFromUI(internalUsers: (IUser | string)[]): Promise {
+ const usernames = internalUsers.map((user) => {
+ if (typeof user === 'string') {
+ return user;
+ }
+ return user.username;
+ });
+ const isThereAnyFederatedUser =
+ usernames.some((username) => username?.includes(':')) ||
+ internalUsers.filter((user) => typeof user !== 'string').some((user) => (user as IUser).federated);
+ if (isThereAnyFederatedUser) {
+ throw new Error('error-this-is-an-ee-feature');
+ }
+ }
+}
diff --git a/apps/meteor/app/federation-v2/server/application/input/RoomReceiverDto.ts b/apps/meteor/app/federation-v2/server/application/input/RoomReceiverDto.ts
new file mode 100644
index 000000000000..ae0b2ace785e
--- /dev/null
+++ b/apps/meteor/app/federation-v2/server/application/input/RoomReceiverDto.ts
@@ -0,0 +1,67 @@
+import { RoomType } from '@rocket.chat/apps-engine/definition/rooms';
+
+import { EVENT_ORIGIN } from '../../domain/IFederationBridge';
+
+class BaseRoom {
+ externalRoomId: string;
+
+ normalizedRoomId: string;
+}
+
+export class FederationRoomCreateInputDto extends BaseRoom {
+ externalInviterId: string;
+
+ normalizedInviterId: string;
+
+ wasInternallyProgramaticallyCreated?: boolean;
+
+ externalRoomName?: string;
+
+ roomType?: RoomType;
+}
+
+export class FederationRoomChangeMembershipDto extends BaseRoom {
+ externalInviterId: string;
+
+ normalizedInviterId: string;
+
+ inviterUsernameOnly: string;
+
+ externalInviteeId: string;
+
+ normalizedInviteeId: string;
+
+ inviteeUsernameOnly: string;
+
+ roomType: RoomType;
+
+ eventOrigin: EVENT_ORIGIN;
+
+ leave?: boolean;
+
+ externalRoomName?: string;
+}
+
+export class FederationRoomSendInternalMessageDto extends BaseRoom {
+ externalSenderId: string;
+
+ normalizedSenderId: string;
+
+ text: string;
+}
+
+export class FederationRoomChangeJoinRulesDto extends BaseRoom {
+ roomType: RoomType;
+}
+
+export class FederationRoomChangeNameDto extends BaseRoom {
+ normalizedRoomName: string;
+
+ externalSenderId: string;
+}
+
+export class FederationRoomChangeTopicDto extends BaseRoom {
+ roomTopic: string;
+
+ externalSenderId: string;
+}
diff --git a/apps/meteor/app/federation-v2/server/application/input/RoomSenderDto.ts b/apps/meteor/app/federation-v2/server/application/input/RoomSenderDto.ts
new file mode 100644
index 000000000000..af4c9de94d0b
--- /dev/null
+++ b/apps/meteor/app/federation-v2/server/application/input/RoomSenderDto.ts
@@ -0,0 +1,29 @@
+import { IMessage } from '@rocket.chat/core-typings';
+
+export class FederationCreateDMAndInviteUserDto {
+ internalInviterId: string;
+
+ internalRoomId: string;
+
+ rawInviteeId: string;
+
+ normalizedInviteeId: string;
+
+ inviteeUsernameOnly: string;
+}
+
+export class FederationRoomSendExternalMessageDto {
+ internalRoomId: string;
+
+ internalSenderId: string;
+
+ message: IMessage;
+}
+
+export class FederationAfterLeaveRoomDto {
+ internalRoomId: string;
+
+ internalUserId: string;
+
+ whoRemovedInternalId?: string;
+}
diff --git a/apps/meteor/app/federation-v2/server/bridge.ts b/apps/meteor/app/federation-v2/server/bridge.ts
deleted file mode 100644
index 895e472de021..000000000000
--- a/apps/meteor/app/federation-v2/server/bridge.ts
+++ /dev/null
@@ -1,85 +0,0 @@
-import type { Bridge as MatrixBridge } from '@rocket.chat/forked-matrix-appservice-bridge';
-
-import { settings } from '../../settings/server';
-import { Settings } from '../../models/server/raw';
-import type { IMatrixEvent } from './definitions/IMatrixEvent';
-import type { MatrixEventType } from './definitions/MatrixEventType';
-import { addToQueue } from './queue';
-import { getRegistrationInfo } from './config';
-import { bridgeLogger } from './logger';
-
-class Bridge {
- private bridgeInstance: MatrixBridge;
-
- private isRunning = false;
-
- public async start(): Promise {
- try {
- await this.stop();
- await this.createInstance();
-
- if (!this.isRunning) {
- await this.bridgeInstance.run(this.getBridgePort());
- this.isRunning = true;
- }
- } catch (e) {
- bridgeLogger.error('Failed to initialize the matrix-appservice-bridge.', e);
-
- bridgeLogger.error('Disabling Matrix Bridge. Please resolve error and try again');
- Settings.updateValueById('Federation_Matrix_enabled', false);
- }
- }
-
- public async stop(): Promise {
- if (!this.isRunning) {
- return;
- }
- // the http server can take some minutes to shutdown and this promise to be resolved
- await this.bridgeInstance?.close();
- this.isRunning = false;
- }
-
- public async getRoomStateByRoomId(userId: string, roomId: string): Promise[]> {
- return Array.from(((await this.getInstance().getIntent(userId).roomState(roomId)) as IMatrixEvent[]) || []);
- }
-
- public getInstance(): MatrixBridge {
- return this.bridgeInstance;
- }
-
- private async createInstance(): Promise {
- bridgeLogger.info('Performing Dynamic Import of matrix-appservice-bridge');
-
- // Dynamic import to prevent Rocket.Chat from loading the module until needed and then handle if that fails
- const { Bridge: MatrixBridge, AppServiceRegistration } = await import('@rocket.chat/forked-matrix-appservice-bridge');
-
- this.bridgeInstance = new MatrixBridge({
- homeserverUrl: settings.get('Federation_Matrix_homeserver_url'),
- domain: settings.get('Federation_Matrix_homeserver_domain'),
- registration: AppServiceRegistration.fromObject(getRegistrationInfo()),
- disableStores: true,
- controller: {
- onAliasQuery: (alias, matrixRoomId): void => {
- console.log('onAliasQuery', alias, matrixRoomId);
- },
- onEvent: async (request /* , context*/): Promise => {
- // Get the event
- const event = request.getData() as unknown as IMatrixEvent;
-
- addToQueue(event);
- },
- onLog: async (line, isError): Promise => {
- console.log(line, isError);
- },
- },
- });
- }
-
- private getBridgePort(): number {
- const [, , port] = settings.get('Federation_Matrix_bridge_url').split(':');
-
- return parseInt(port);
- }
-}
-
-export const matrixBridge = new Bridge();
diff --git a/apps/meteor/app/federation-v2/server/config.ts b/apps/meteor/app/federation-v2/server/config.ts
deleted file mode 100644
index d1bad2455d80..000000000000
--- a/apps/meteor/app/federation-v2/server/config.ts
+++ /dev/null
@@ -1,43 +0,0 @@
-import type { AppServiceOutput } from '@rocket.chat/forked-matrix-appservice-bridge';
-
-import { settings } from '../../settings/server';
-
-export type bridgeUrlTuple = [string, string, number];
-
-export function getRegistrationInfo(): AppServiceOutput {
- /* eslint-disable @typescript-eslint/camelcase */
- return {
- id: settings.get('Federation_Matrix_id'),
- hs_token: settings.get('Federation_Matrix_hs_token'),
- as_token: settings.get('Federation_Matrix_as_token'),
- url: settings.get('Federation_Matrix_bridge_url'),
- sender_localpart: settings.get('Federation_Matrix_bridge_localpart'),
- namespaces: {
- users: [
- {
- exclusive: false,
- // Reserve these MXID's (usernames)
- regex: `.*`,
- },
- ],
- aliases: [
- {
- exclusive: false,
- // Reserve these room aliases
- regex: `.*`,
- },
- ],
- rooms: [
- {
- exclusive: false,
- // This regex is used to define which rooms we listen to with the bridge.
- // This does not reserve the rooms like the other namespaces.
- regex: '.*',
- },
- ],
- },
- rate_limited: false,
- protocols: null,
- };
- /* eslint-enable @typescript-eslint/camelcase */
-}
diff --git a/apps/meteor/app/federation-v2/server/data-interface/index.ts b/apps/meteor/app/federation-v2/server/data-interface/index.ts
deleted file mode 100644
index 18e2fbf7020f..000000000000
--- a/apps/meteor/app/federation-v2/server/data-interface/index.ts
+++ /dev/null
@@ -1,9 +0,0 @@
-import * as message from './message';
-import * as room from './room';
-import * as user from './user';
-
-export const dataInterface = {
- message: message.normalize,
- room: room.normalize,
- user: user.normalize,
-};
diff --git a/apps/meteor/app/federation-v2/server/data-interface/message.ts b/apps/meteor/app/federation-v2/server/data-interface/message.ts
deleted file mode 100644
index 7d27732f93e6..000000000000
--- a/apps/meteor/app/federation-v2/server/data-interface/message.ts
+++ /dev/null
@@ -1,17 +0,0 @@
-import { IMessage, IUser } from '@rocket.chat/core-typings';
-
-import { dataInterface } from '.';
-
-interface INormalizedMessage extends IMessage {
- u: Required>;
-}
-
-export const normalize = async (message: IMessage): Promise => {
- // TODO: normalize the entire payload (if needed)
- const normalizedMessage: INormalizedMessage = message as INormalizedMessage;
-
- // Normalize the user
- normalizedMessage.u = (await dataInterface.user(message.u._id)) as Required>;
-
- return normalizedMessage;
-};
diff --git a/apps/meteor/app/federation-v2/server/data-interface/room.ts b/apps/meteor/app/federation-v2/server/data-interface/room.ts
deleted file mode 100644
index df1d2163badf..000000000000
--- a/apps/meteor/app/federation-v2/server/data-interface/room.ts
+++ /dev/null
@@ -1,8 +0,0 @@
-import { IRoom } from '@rocket.chat/core-typings';
-
-import { Rooms } from '../../../models/server';
-
-export const normalize = async (roomId: string): Promise => {
- // Normalize the user
- return Rooms.findOneById(roomId);
-};
diff --git a/apps/meteor/app/federation-v2/server/data-interface/user.ts b/apps/meteor/app/federation-v2/server/data-interface/user.ts
deleted file mode 100644
index 15fb48843428..000000000000
--- a/apps/meteor/app/federation-v2/server/data-interface/user.ts
+++ /dev/null
@@ -1,8 +0,0 @@
-import { IUser } from '@rocket.chat/core-typings';
-
-import { Users } from '../../../models/server';
-
-export const normalize = async (userId: string): Promise => {
- // Normalize the user
- return Users.findOneById(userId);
-};
diff --git a/apps/meteor/app/federation-v2/server/definitions/IMatrixEventContent/IMatrixEventContentCreateRoom.ts b/apps/meteor/app/federation-v2/server/definitions/IMatrixEventContent/IMatrixEventContentCreateRoom.ts
deleted file mode 100644
index 6180c0356200..000000000000
--- a/apps/meteor/app/federation-v2/server/definitions/IMatrixEventContent/IMatrixEventContentCreateRoom.ts
+++ /dev/null
@@ -1,5 +0,0 @@
-export interface IMatrixEventContentCreateRoom {
- creator: string;
- room_version: string;
- was_programatically_created?: boolean;
-}
diff --git a/apps/meteor/app/federation-v2/server/definitions/IMatrixEventContent/IMatrixEventContentSetRoomJoinRules.ts b/apps/meteor/app/federation-v2/server/definitions/IMatrixEventContent/IMatrixEventContentSetRoomJoinRules.ts
deleted file mode 100644
index 920f9bb53777..000000000000
--- a/apps/meteor/app/federation-v2/server/definitions/IMatrixEventContent/IMatrixEventContentSetRoomJoinRules.ts
+++ /dev/null
@@ -1,8 +0,0 @@
-export enum SetRoomJoinRules {
- JOIN = 'public',
- INVITE = 'invite',
-}
-
-export interface IMatrixEventContentSetRoomJoinRules {
- join_rule: SetRoomJoinRules;
-}
diff --git a/apps/meteor/app/federation-v2/server/definitions/IMatrixEventContent/index.ts b/apps/meteor/app/federation-v2/server/definitions/IMatrixEventContent/index.ts
deleted file mode 100644
index 7615779282e1..000000000000
--- a/apps/meteor/app/federation-v2/server/definitions/IMatrixEventContent/index.ts
+++ /dev/null
@@ -1,16 +0,0 @@
-import { MatrixEventType } from '../MatrixEventType';
-import { IMatrixEventContentCreateRoom } from './IMatrixEventContentCreateRoom';
-import { IMatrixEventContentAddMemberToRoom } from './IMatrixEventContentAddMemberToRoom';
-import { IMatrixEventContentSendMessage } from './IMatrixEventContentSendMessage';
-import { IMatrixEventContentSetRoomJoinRules } from './IMatrixEventContentSetRoomJoinRules';
-import { IMatrixEventContentSetRoomName } from './IMatrixEventContentSetRoomName';
-import { IMatrixEventContentSetRoomTopic } from './IMatrixEventContentSetRoomTopic';
-
-export type EventContent = {
- [MatrixEventType.CREATE_ROOM]: IMatrixEventContentCreateRoom;
- [MatrixEventType.ROOM_MEMBERSHIP]: IMatrixEventContentAddMemberToRoom;
- [MatrixEventType.SET_ROOM_JOIN_RULES]: IMatrixEventContentSetRoomJoinRules;
- [MatrixEventType.SET_ROOM_NAME]: IMatrixEventContentSetRoomName;
- [MatrixEventType.SET_ROOM_TOPIC]: IMatrixEventContentSetRoomTopic;
- [MatrixEventType.SEND_MESSAGE]: IMatrixEventContentSendMessage;
-};
diff --git a/apps/meteor/app/federation-v2/server/definitions/MatrixEventType.ts b/apps/meteor/app/federation-v2/server/definitions/MatrixEventType.ts
deleted file mode 100644
index 14d4f0bb0ecb..000000000000
--- a/apps/meteor/app/federation-v2/server/definitions/MatrixEventType.ts
+++ /dev/null
@@ -1,12 +0,0 @@
-export enum MatrixEventType {
- CREATE_ROOM = 'm.room.create',
- ROOM_MEMBERSHIP = 'm.room.member',
- // SET_ROOM_POWER_LEVELS = 'm.room.power_levels',
- // SET_ROOM_CANONICAL_ALIAS = 'm.room.canonical_alias',
- SET_ROOM_JOIN_RULES = 'm.room.join_rules',
- // SET_ROOM_HISTORY_VISIBILITY = 'm.room.history_visibility',
- // SET_ROOM_GUEST_ACCESS = 'm.room.guest_access',
- SET_ROOM_NAME = 'm.room.name',
- SET_ROOM_TOPIC = 'm.room.topic',
- SEND_MESSAGE = 'm.room.message',
-}
diff --git a/apps/meteor/app/federation-v2/server/domain/FederatedRoom.ts b/apps/meteor/app/federation-v2/server/domain/FederatedRoom.ts
new file mode 100644
index 000000000000..aec246f23788
--- /dev/null
+++ b/apps/meteor/app/federation-v2/server/domain/FederatedRoom.ts
@@ -0,0 +1,88 @@
+import { RoomType } from '@rocket.chat/apps-engine/definition/rooms';
+import { IRoom, IUser } from '@rocket.chat/core-typings';
+
+import { FederatedUser } from './FederatedUser';
+
+export class FederatedRoom {
+ public externalId: string;
+
+ public members?: FederatedUser[];
+
+ public internalReference: IRoom;
+
+ // eslint-disable-next-line
+ protected constructor() {}
+
+ protected static generateTemporaryName(normalizedExternalId: string): string {
+ return `Federation-${normalizedExternalId}`;
+ }
+
+ public static createInstance(
+ externalId: string,
+ normalizedExternalId: string,
+ creator: FederatedUser,
+ type: RoomType,
+ name?: string,
+ members?: FederatedUser[],
+ ): FederatedRoom {
+ const roomName = name || FederatedRoom.generateTemporaryName(normalizedExternalId);
+ return Object.assign(new FederatedRoom(), {
+ externalId,
+ members,
+ internalReference: {
+ t: type,
+ name: roomName,
+ fname: roomName,
+ u: creator.internalReference,
+ },
+ });
+ }
+
+ public static build(): FederatedRoom {
+ return new FederatedRoom();
+ }
+
+ public getMembers(): IUser[] {
+ return this.members && this.members.length > 0 ? this.members.map((user) => user.internalReference) : [];
+ }
+
+ public isFederated(): boolean {
+ return this.internalReference?.federated === true;
+ }
+
+ public isDirectMessage(): boolean {
+ return this.internalReference?.t === RoomType.DIRECT_MESSAGE;
+ }
+
+ public static buildRoomIdForDirectMessages(inviter: FederatedUser, invitee: FederatedUser): string {
+ if (!inviter.internalReference || !invitee.internalReference) {
+ throw new Error('Cannot create room Id without the user ids');
+ }
+ return [inviter.internalReference, invitee.internalReference]
+ .map(({ _id }) => _id)
+ .sort()
+ .join('');
+ }
+
+ public setRoomType(type: RoomType): void {
+ if (this.isDirectMessage()) {
+ throw new Error('Its not possible to change a direct message type');
+ }
+ this.internalReference.t = type;
+ }
+
+ public changeRoomName(name: string): void {
+ if (this.isDirectMessage()) {
+ throw new Error('Its not possible to change a direct message name');
+ }
+ this.internalReference.name = name;
+ this.internalReference.fname = name;
+ }
+
+ public changeRoomTopic(topic: string): void {
+ if (this.isDirectMessage()) {
+ throw new Error('Its not possible to change a direct message topic');
+ }
+ this.internalReference.topic = topic;
+ }
+}
diff --git a/apps/meteor/app/federation-v2/server/domain/FederatedUser.ts b/apps/meteor/app/federation-v2/server/domain/FederatedUser.ts
new file mode 100644
index 000000000000..681f298038c9
--- /dev/null
+++ b/apps/meteor/app/federation-v2/server/domain/FederatedUser.ts
@@ -0,0 +1,38 @@
+import { IUser } from '@rocket.chat/core-typings';
+
+export interface IFederatedUserCreationParams {
+ name: string;
+ username: string;
+ existsOnlyOnProxyServer: boolean;
+}
+
+export class FederatedUser {
+ public externalId: string;
+
+ public internalReference: IUser;
+
+ public existsOnlyOnProxyServer: boolean;
+
+ // eslint-disable-next-line
+ protected constructor() {}
+
+ public static createInstance(externalId: string, params: IFederatedUserCreationParams): FederatedUser {
+ return Object.assign(new FederatedUser(), {
+ externalId,
+ existsOnlyOnProxyServer: params.existsOnlyOnProxyServer,
+ internalReference: {
+ username: params.username,
+ name: params.name,
+ type: 'user',
+ status: 'online',
+ active: true,
+ roles: ['user'],
+ requirePasswordChange: false,
+ },
+ });
+ }
+
+ public static build(): FederatedUser {
+ return new FederatedUser();
+ }
+}
diff --git a/apps/meteor/app/federation-v2/server/domain/IFederationBridge.ts b/apps/meteor/app/federation-v2/server/domain/IFederationBridge.ts
new file mode 100644
index 000000000000..66af6beabf01
--- /dev/null
+++ b/apps/meteor/app/federation-v2/server/domain/IFederationBridge.ts
@@ -0,0 +1,20 @@
+export interface IFederationBridge {
+ start(): Promise;
+ stop(): Promise;
+ onFederationAvailabilityChanged(enabled: boolean): Promise;
+ getUserProfileInformation(externalUserId: string): Promise;
+ joinRoom(externalRoomId: string, externalUserId: string): Promise;
+ createDirectMessageRoom(externalCreatorId: string, externalInviteeIds: string[]): Promise;
+ inviteToRoom(externalRoomId: string, externalInviterId: string, externalInviteeId: string): Promise;
+ sendMessage(externalRoomId: string, externaSenderId: string, text: string): Promise;
+ createUser(username: string, name: string, domain: string): Promise;
+ isUserIdFromTheSameHomeserver(externalUserId: string, domain: string): boolean;
+ isRoomFromTheSameHomeserver(externalRoomId: string, domain: string): boolean;
+ leaveRoom(externalRoomId: string, externalUserId: string): Promise;
+ kickUserFromRoom(externalRoomId: string, externalUserId: string, externalOwnerId: string): Promise;
+}
+
+export enum EVENT_ORIGIN {
+ LOCAL = 'LOCAL',
+ REMOTE = 'REMOTE',
+}
diff --git a/apps/meteor/app/federation-v2/server/eventHandler.ts b/apps/meteor/app/federation-v2/server/eventHandler.ts
deleted file mode 100644
index 166ed1199adc..000000000000
--- a/apps/meteor/app/federation-v2/server/eventHandler.ts
+++ /dev/null
@@ -1,50 +0,0 @@
-import { IMatrixEvent } from './definitions/IMatrixEvent';
-import { MatrixEventType } from './definitions/MatrixEventType';
-import { handleRoomMembership, handleCreateRoom, handleSendMessage, setRoomJoinRules, setRoomName, setRoomTopic } from './events';
-
-export const eventHandler = async (event: IMatrixEvent): Promise => {
- console.log(`Processing ${event.type}...`, JSON.stringify(event, null, 2));
-
- switch (event.type) {
- case MatrixEventType.CREATE_ROOM: {
- await handleCreateRoom(event as IMatrixEvent);
-
- break;
- }
- case MatrixEventType.ROOM_MEMBERSHIP: {
- await handleRoomMembership(event as IMatrixEvent);
-
- break;
- }
- case MatrixEventType.SET_ROOM_JOIN_RULES: {
- await setRoomJoinRules(event as IMatrixEvent);
-
- break;
- }
- case MatrixEventType.SET_ROOM_NAME: {
- await setRoomName(event as IMatrixEvent);
-
- break;
- }
- case MatrixEventType.SET_ROOM_TOPIC: {
- await setRoomTopic(event as IMatrixEvent);
-
- break;
- }
- case MatrixEventType.SEND_MESSAGE: {
- await handleSendMessage(event as IMatrixEvent);
-
- break;
- }
- // case MatrixEventType.SET_ROOM_POWER_LEVELS:
- // case MatrixEventType.SET_ROOM_CANONICAL_ALIAS:
- // case MatrixEventType.SET_ROOM_HISTORY_VISIBILITY:
- // case MatrixEventType.SET_ROOM_GUEST_ACCESS: {
- // console.log(`Ignoring ${event.type}`);
- //
- // break;
- // }
- default:
- console.log(`Could not find handler for ${event.type}`, event);
- }
-};
diff --git a/apps/meteor/app/federation-v2/server/events/createRoom.ts b/apps/meteor/app/federation-v2/server/events/createRoom.ts
deleted file mode 100644
index 5cca3032c455..000000000000
--- a/apps/meteor/app/federation-v2/server/events/createRoom.ts
+++ /dev/null
@@ -1,187 +0,0 @@
-import { IRoom, RoomType } from '@rocket.chat/apps-engine/definition/rooms';
-import { ICreatedRoom } from '@rocket.chat/core-typings';
-import { IUser } from '@rocket.chat/apps-engine/definition/users';
-
-import { MatrixBridgedRoom, MatrixBridgedUser, Users } from '../../../models/server';
-import { Rooms } from '../../../models/server/raw';
-import { createRoom } from '../../../lib/server';
-import { IMatrixEvent } from '../definitions/IMatrixEvent';
-import { MatrixEventType } from '../definitions/MatrixEventType';
-import { checkBridgedRoomExists } from '../methods/checkBridgedRoomExists';
-import { matrixClient } from '../matrix-client';
-import { SetRoomJoinRules } from '../definitions/IMatrixEventContent/IMatrixEventContentSetRoomJoinRules';
-import { matrixBridge } from '../bridge';
-import { setRoomJoinRules } from './setRoomJoinRules';
-import { setRoomName } from './setRoomName';
-import { handleRoomMembership } from './roomMembership';
-
-const removeUselessCharacterFromMatrixRoomId = (matrixRoomId: string): string => {
- const prefixedRoomIdOnly = matrixRoomId.split(':')[0];
- const prefix = '!';
-
- return prefixedRoomIdOnly?.replace(prefix, '');
-};
-
-const generateRoomNameForLocalServer = (matrixRoomId: string, matrixRoomName?: string): string => {
- return matrixRoomName || `Federation-${removeUselessCharacterFromMatrixRoomId(matrixRoomId)}`;
-};
-
-const createLocalRoomAsync = async (roomType: RoomType, roomName: string, creator: IUser, members: IUser[] = []): Promise => {
- return new Promise((resolve) => resolve(createRoom(roomType, roomName, creator.username, members as any[]) as ICreatedRoom));
-};
-
-const createBridgedRecordRoom = async (roomId: IRoom['id'], matrixRoomId: string): Promise =>
- new Promise((resolve) => resolve(MatrixBridgedRoom.insert({ rid: roomId, mri: matrixRoomId })));
-
-const createLocalUserIfNecessary = async (matrixUserId: string): Promise => {
- const { uid } = await matrixClient.user.createLocal(matrixUserId);
-
- return uid;
-};
-
-const applyRoomStateIfNecessary = async (matrixRoomId: string, roomState?: IMatrixEvent[]): Promise => {
- // TODO: this should be better
- /* eslint-disable no-await-in-loop */
- for (const state of roomState || []) {
- switch (state.type) {
- case 'm.room.create':
- continue;
- case 'm.room.join_rules': {
- // @ts-ignore
- // eslint-disable-next-line @typescript-eslint/camelcase
- await setRoomJoinRules({ room_id: matrixRoomId, ...state });
-
- break;
- }
- case 'm.room.name': {
- // @ts-ignore
- // eslint-disable-next-line @typescript-eslint/camelcase
- await setRoomName({ room_id: matrixRoomId, ...state });
-
- break;
- }
- case 'm.room.member': {
- // @ts-ignore
- if (state.content.membership === 'join') {
- // @ts-ignore
- // eslint-disable-next-line @typescript-eslint/camelcase,@typescript-eslint/no-use-before-define
- await handleRoomMembership({ room_id: matrixRoomId, ...state });
- }
-
- break;
- }
- }
- }
- /* eslint-enable no-await-in-loop */
-};
-
-const mapLocalAndExternal = async (roomId: string, matrixRoomId: string): Promise => {
- await createBridgedRecordRoom(roomId, matrixRoomId);
- await Rooms.setAsBridged(roomId);
-};
-
-const tryToGetDataFromExternalRoom = async (
- senderMatrixUserId: string,
- matrixRoomId: string,
- roomState: IMatrixEvent[] = [],
-): Promise> => {
- const finalRoomState =
- roomState && roomState?.length > 0 ? roomState : await matrixBridge.getRoomStateByRoomId(senderMatrixUserId, matrixRoomId);
- const externalRoomName = finalRoomState.find((stateEvent: Record) => stateEvent.type === MatrixEventType.SET_ROOM_NAME)
- ?.content?.name;
- const externalRoomJoinRule = finalRoomState.find(
- (stateEvent: Record) => stateEvent.type === MatrixEventType.SET_ROOM_JOIN_RULES,
- )?.content?.join_rule;
-
- return {
- externalRoomName,
- externalRoomJoinRule,
- };
-};
-
-export const createLocalDirectMessageRoom = async (matrixRoomId: string, creator: IUser, affectedUser: IUser): Promise => {
- const { _id: roomId } = await createLocalRoomAsync(RoomType.DIRECT_MESSAGE, generateRoomNameForLocalServer(matrixRoomId), creator, [
- creator,
- affectedUser,
- ]);
- await mapLocalAndExternal(roomId, matrixRoomId);
-
- return roomId;
-};
-
-export const getLocalRoomType = (matrixJoinRule = '', matrixRoomIsDirect = false): RoomType => {
- const mapping: Record = {
- [SetRoomJoinRules.JOIN]: RoomType.CHANNEL,
- [SetRoomJoinRules.INVITE]: RoomType.PRIVATE_GROUP,
- };
- const roomType = mapping[matrixJoinRule] || RoomType.CHANNEL;
-
- return roomType === RoomType.PRIVATE_GROUP && matrixRoomIsDirect ? RoomType.DIRECT_MESSAGE : roomType;
-};
-
-export const createLocalChannelsRoom = async (
- matrixRoomId: string,
- senderMatrixUserId: string,
- creator: IUser,
- roomState?: IMatrixEvent[],
-): Promise => {
- let roomName = '';
- let joinRule;
-
- try {
- const { externalRoomName, externalRoomJoinRule } = await tryToGetDataFromExternalRoom(senderMatrixUserId, matrixRoomId, roomState);
- roomName = externalRoomName;
- joinRule = externalRoomJoinRule;
- } catch (err) {
- // no-op
- }
- const { rid: roomId } = await createLocalRoomAsync(
- getLocalRoomType(joinRule),
- generateRoomNameForLocalServer(matrixRoomId, roomName),
- creator,
- );
- await mapLocalAndExternal(roomId, matrixRoomId);
-
- return roomId;
-};
-
-export const processFirstAccessFromExternalServer = async (
- matrixRoomId: string,
- senderMatrixUserId: string,
- affectedMatrixUserId: string,
- senderUser: IUser,
- affectedUser: IUser,
- isDirect = false,
- roomState: IMatrixEvent[],
-): Promise => {
- let roomId;
- if (isDirect) {
- roomId = await createLocalDirectMessageRoom(matrixRoomId, senderUser, affectedUser);
- } else {
- roomId = await createLocalChannelsRoom(matrixRoomId, senderMatrixUserId, senderUser, roomState);
- }
-
- await applyRoomStateIfNecessary(matrixRoomId, roomState);
- await matrixBridge.getInstance().getIntent(affectedMatrixUserId).join(matrixRoomId);
-
- return roomId;
-};
-
-export const handleCreateRoom = async (event: IMatrixEvent): Promise