From f45b9563f465ca043ba6c1cfbabe7b5ea8bc72d7 Mon Sep 17 00:00:00 2001 From: Guilherme Gazzo Date: Sat, 6 Aug 2022 01:58:09 -0300 Subject: [PATCH] Chore: Remove settings Fibers usage (#26465) Co-authored-by: Diego Sampaio --- .../app/apps/server/communication/rest.js | 38 +-- apps/meteor/app/assets/server/assets.ts | 4 +- .../authentication/server/startup/index.js | 6 +- .../server/functions/buildRegistrationData.ts | 6 +- ...onnectWorkspace.js => connectWorkspace.ts} | 10 +- ...ectWorkspace.js => disconnectWorkspace.ts} | 7 +- ...tionUrl.js => getOAuthAuthorizationUrl.ts} | 10 +- ...essToken.js => getWorkspaceAccessToken.ts} | 19 +- ...spaceLicense.js => getWorkspaceLicense.ts} | 16 +- ...Workspace.js => startRegisterWorkspace.ts} | 10 +- .../{syncWorkspace.js => syncWorkspace.ts} | 20 +- .../server/functions/unregisterWorkspace.js | 19 -- .../server/functions/unregisterWorkspace.ts | 22 ++ apps/meteor/app/cloud/server/index.js | 2 +- .../server/lib/RocketChat.ErrorHandler.js | 3 +- .../app/file-upload/server/lib/FileUpload.js | 7 +- .../app/importer-csv/server/importer.js | 5 +- .../server/importer.js | 4 +- .../importer-slack-users/server/importer.js | 5 +- .../app/importer-slack/server/importer.js | 5 +- .../importer/server/classes/ImporterBase.js | 33 ++- .../meteor/app/irc/server/irc-bridge/index.js | 4 +- ...IrcConnection.js => resetIrcConnection.ts} | 21 +- ...itationEmail.js => sendInvitationEmail.ts} | 21 +- .../livechat/imports/server/rest/upload.js | 10 +- .../app/livechat/server/api/v1/videoCall.js | 29 +- .../app/livechat/server/lib/Livechat.js | 71 ++--- .../app/livechat/server/methods/facebook.js | 2 +- .../{saveAppearance.js => saveAppearance.ts} | 17 +- .../server/methods/saveIntegration.js | 55 ---- .../server/methods/saveIntegration.ts | 56 ++++ apps/meteor/app/mailer/server/api.ts | 4 +- apps/meteor/app/models/server/index.ts | 2 - .../app/models/server/models/Settings.js | 280 ------------------ .../app/settings/server/CachedSettings.ts | 9 +- .../app/settings/server/SettingsRegistry.ts | 22 +- .../server/functions/settings.mocks.ts | 6 +- apps/meteor/app/settings/server/index.ts | 16 +- apps/meteor/app/settings/server/raw.js | 31 -- apps/meteor/app/settings/server/raw.ts | 32 ++ apps/meteor/app/settings/server/startup.ts | 8 +- .../server/functions/updateStatsCounter.ts | 3 +- .../app/statistics/server/lib/statistics.ts | 38 ++- apps/meteor/app/theme/server/server.js | 2 +- apps/meteor/app/ui-master/server/index.js | 14 +- .../server/functions/checkVersionUpdate.js | 93 ------ .../server/functions/checkVersionUpdate.ts | 90 ++++++ .../server/functions/getNewUpdates.js | 72 ----- .../server/functions/getNewUpdates.ts | 93 ++++++ apps/meteor/app/version-check/server/index.ts | 6 +- .../externals/meteor/rocketchat-tap-i18n.d.ts | 9 +- .../ee/app/canned-responses/server/index.js | 6 +- .../app/license/server/getSeatsRequestLink.ts | 9 +- apps/meteor/ee/app/license/server/settings.js | 2 +- .../livechat-enterprise/server/settings.ts | 9 +- .../meteor/ee/app/settings/server/settings.ts | 4 +- apps/meteor/ee/server/api/licenses.ts | 7 +- apps/meteor/ee/server/requestSeatsRoute.ts | 2 +- apps/meteor/ee/server/startup/upsell.ts | 2 +- apps/meteor/server/cron/federation.ts | 2 +- apps/meteor/server/cron/statistics.js | 2 +- .../meteor/server/lib/sendMessagesToAdmins.ts | 2 +- ...ameters.js => getSetupWizardParameters.ts} | 6 +- apps/meteor/server/models/raw/Settings.ts | 9 +- .../services/nps/getAndCreateNpsSurvey.ts | 2 +- .../server/services/nps/sendNpsResults.ts | 2 +- apps/meteor/server/startup/migrations/v246.ts | 9 +- .../server/functions/settings.tests.ts | 92 +++--- .../unit/app/settings/server/raw.tests.js | 45 --- packages/core-typings/src/ISetting.ts | 2 +- .../src/models/ISettingsModel.ts | 7 +- 71 files changed, 684 insertions(+), 904 deletions(-) rename apps/meteor/app/cloud/server/functions/{connectWorkspace.js => connectWorkspace.ts} (85%) rename apps/meteor/app/cloud/server/functions/{disconnectWorkspace.js => disconnectWorkspace.ts} (55%) rename apps/meteor/app/cloud/server/functions/{getOAuthAuthorizationUrl.js => getOAuthAuthorizationUrl.ts} (57%) rename apps/meteor/app/cloud/server/functions/{getWorkspaceAccessToken.js => getWorkspaceAccessToken.ts} (52%) rename apps/meteor/app/cloud/server/functions/{getWorkspaceLicense.js => getWorkspaceLicense.ts} (72%) rename apps/meteor/app/cloud/server/functions/{startRegisterWorkspace.js => startRegisterWorkspace.ts} (81%) rename apps/meteor/app/cloud/server/functions/{syncWorkspace.js => syncWorkspace.ts} (81%) delete mode 100644 apps/meteor/app/cloud/server/functions/unregisterWorkspace.js create mode 100644 apps/meteor/app/cloud/server/functions/unregisterWorkspace.ts rename apps/meteor/app/irc/server/methods/{resetIrcConnection.js => resetIrcConnection.ts} (75%) rename apps/meteor/app/lib/server/methods/{sendInvitationEmail.js => sendInvitationEmail.ts} (71%) rename apps/meteor/app/livechat/server/methods/{saveAppearance.js => saveAppearance.ts} (72%) delete mode 100644 apps/meteor/app/livechat/server/methods/saveIntegration.js create mode 100644 apps/meteor/app/livechat/server/methods/saveIntegration.ts delete mode 100644 apps/meteor/app/models/server/models/Settings.js delete mode 100644 apps/meteor/app/settings/server/raw.js create mode 100644 apps/meteor/app/settings/server/raw.ts delete mode 100644 apps/meteor/app/version-check/server/functions/checkVersionUpdate.js create mode 100644 apps/meteor/app/version-check/server/functions/checkVersionUpdate.ts delete mode 100644 apps/meteor/app/version-check/server/functions/getNewUpdates.js create mode 100644 apps/meteor/app/version-check/server/functions/getNewUpdates.ts rename apps/meteor/server/methods/{getSetupWizardParameters.js => getSetupWizardParameters.ts} (66%) delete mode 100644 apps/meteor/tests/unit/app/settings/server/raw.tests.js diff --git a/apps/meteor/app/apps/server/communication/rest.js b/apps/meteor/app/apps/server/communication/rest.js index 158dbe3d8ae..79276a15a8b 100644 --- a/apps/meteor/app/apps/server/communication/rest.js +++ b/apps/meteor/app/apps/server/communication/rest.js @@ -70,13 +70,13 @@ export class AppsRestApi { '', { authRequired: true, permissionsRequired: ['manage-apps'] }, { - get() { + async get() { const baseUrl = orchestrator.getMarketplaceUrl(); // Gets the Apps from the marketplace if (this.queryParams.marketplace) { const headers = getDefaultHeaders(); - const token = getWorkspaceAccessToken(); + const token = await getWorkspaceAccessToken(); if (token) { headers.Authorization = `Bearer ${token}`; } @@ -100,7 +100,7 @@ export class AppsRestApi { if (this.queryParams.categories) { const headers = getDefaultHeaders(); - const token = getWorkspaceAccessToken(); + const token = await getWorkspaceAccessToken(); if (token) { headers.Authorization = `Bearer ${token}`; } @@ -183,8 +183,8 @@ export class AppsRestApi { const headers = getDefaultHeaders(); try { - const downloadToken = getWorkspaceAccessToken(true, 'marketplace:download', false); - const marketplaceToken = getWorkspaceAccessToken(); + const downloadToken = await getWorkspaceAccessToken(true, 'marketplace:download', false); + const marketplaceToken = await getWorkspaceAccessToken(); const [downloadResponse, marketplaceResponse] = await Promise.all([ fetch(`${baseUrl}/v2/apps/${this.bodyParams.appId}/download/${this.bodyParams.version}?token=${downloadToken}`, { @@ -318,11 +318,11 @@ export class AppsRestApi { 'bundles/:id/apps', { authRequired: true, permissionsRequired: ['manage-apps'] }, { - get() { + async get() { const baseUrl = orchestrator.getMarketplaceUrl(); const headers = {}; - const token = getWorkspaceAccessToken(); + const token = await getWorkspaceAccessToken(); if (token) { headers.Authorization = `Bearer ${token}`; } @@ -351,11 +351,11 @@ export class AppsRestApi { 'featured', { authRequired: true }, { - get() { + async get() { const baseUrl = orchestrator.getMarketplaceUrl(); const headers = getDefaultHeaders(); - const token = getWorkspaceAccessToken(); + const token = await getWorkspaceAccessToken(); if (token) { headers.Authorization = `Bearer ${token}`; } @@ -383,12 +383,12 @@ export class AppsRestApi { ':id', { authRequired: true, permissionsRequired: ['manage-apps'] }, { - get() { + async get() { if (this.queryParams.marketplace && this.queryParams.version) { const baseUrl = orchestrator.getMarketplaceUrl(); const headers = {}; // DO NOT ATTACH THE FRAMEWORK/ENGINE VERSION HERE. - const token = getWorkspaceAccessToken(); + const token = await getWorkspaceAccessToken(); if (token) { headers.Authorization = `Bearer ${token}`; } @@ -414,7 +414,7 @@ export class AppsRestApi { const baseUrl = orchestrator.getMarketplaceUrl(); const headers = getDefaultHeaders(); - const token = getWorkspaceAccessToken(); + const token = await getWorkspaceAccessToken(); if (token) { headers.Authorization = `Bearer ${token}`; } @@ -466,7 +466,7 @@ export class AppsRestApi { const baseUrl = orchestrator.getMarketplaceUrl(); const headers = getDefaultHeaders(); - const token = getWorkspaceAccessToken(true, 'marketplace:download', false); + const token = await getWorkspaceAccessToken(true, 'marketplace:download', false); try { const response = await fetch( @@ -564,11 +564,11 @@ export class AppsRestApi { ':id/versions', { authRequired: true, permissionsRequired: ['manage-apps'] }, { - get() { + async get() { const baseUrl = orchestrator.getMarketplaceUrl(); const headers = {}; // DO NOT ATTACH THE FRAMEWORK/ENGINE VERSION HERE. - const token = getWorkspaceAccessToken(); + const token = await getWorkspaceAccessToken(); if (token) { headers.Authorization = `Bearer ${token}`; } @@ -596,16 +596,16 @@ export class AppsRestApi { ':id/sync', { authRequired: true, permissionsRequired: ['manage-apps'] }, { - post() { + async post() { const baseUrl = orchestrator.getMarketplaceUrl(); const headers = getDefaultHeaders(); - const token = getWorkspaceAccessToken(); + const token = await getWorkspaceAccessToken(); if (token) { headers.Authorization = `Bearer ${token}`; } - const workspaceIdSetting = Promise.await(Settings.findOneById('Cloud_Workspace_Id')); + const workspaceIdSetting = await Settings.findOneById('Cloud_Workspace_Id'); let result; try { @@ -622,7 +622,7 @@ export class AppsRestApi { return API.v1.failure(); } - Promise.await(Apps.updateAppsMarketplaceInfo([result.data])); + await Apps.updateAppsMarketplaceInfo([result.data]); return API.v1.success({ app: result.data }); }, diff --git a/apps/meteor/app/assets/server/assets.ts b/apps/meteor/app/assets/server/assets.ts index 1262f85041d..d37e67470b1 100644 --- a/apps/meteor/app/assets/server/assets.ts +++ b/apps/meteor/app/assets/server/assets.ts @@ -9,13 +9,13 @@ import sizeOf from 'image-size'; import sharp from 'sharp'; import { NextHandleFunction } from 'connect'; import { IRocketChatAssets, IRocketChatAsset } from '@rocket.chat/core-typings'; +import { Settings } from '@rocket.chat/models'; import { settings, settingsRegistry } from '../../settings/server'; import { getURL } from '../../utils/lib/getURL'; import { getExtension } from '../../utils/lib/mimeTypes'; import { hasPermission } from '../../authorization/server'; import { RocketChatFile } from '../../file'; -import { Settings } from '../../models/server'; const RocketChatAssetsInstance = new RocketChatFile.GridFS({ name: 'assets', @@ -347,7 +347,7 @@ function addAssetToSetting(asset: string, value: IRocketChatAsset): void { if (typeof currentValue === 'object' && currentValue.defaultUrl !== getAssetByKey(asset).defaultUrl) { currentValue.defaultUrl = getAssetByKey(asset).defaultUrl; - Settings.updateValueById(key, currentValue); + Promise.await(Settings.updateValueById(key, currentValue)); } } diff --git a/apps/meteor/app/authentication/server/startup/index.js b/apps/meteor/app/authentication/server/startup/index.js index b508d87dced..536117e232d 100644 --- a/apps/meteor/app/authentication/server/startup/index.js +++ b/apps/meteor/app/authentication/server/startup/index.js @@ -4,12 +4,12 @@ import { Accounts } from 'meteor/accounts-base'; import { TAPi18n } from 'meteor/rocketchat:tap-i18n'; import _ from 'underscore'; import { escapeRegExp, escapeHTML } from '@rocket.chat/string-helpers'; -import { Roles, Users as UsersRaw } from '@rocket.chat/models'; +import { Roles, Settings, Users as UsersRaw } from '@rocket.chat/models'; import * as Mailer from '../../../mailer/server/api'; import { settings } from '../../../settings/server'; import { callbacks } from '../../../../lib/callbacks'; -import { Settings, Users } from '../../../models/server'; +import { Users } from '../../../models/server'; import { addUserRoles } from '../../../../server/lib/roles/addUserRoles'; import { getAvatarSuggestionForUser } from '../../../lib/server/functions/getAvatarSuggestionForUser'; import { parseCSV } from '../../../../lib/utils/parseCSV'; @@ -295,7 +295,7 @@ Accounts.insertUserDoc = _.wrap(Accounts.insertUserDoc, function (insertUserDoc, if (!roles.includes('admin') && !hasAdmin) { roles.push('admin'); if (settings.get('Show_Setup_Wizard') === 'pending') { - Settings.updateValueById('Show_Setup_Wizard', 'in_progress'); + Promise.await(Settings.updateValueById('Show_Setup_Wizard', 'in_progress')); } } diff --git a/apps/meteor/app/cloud/server/functions/buildRegistrationData.ts b/apps/meteor/app/cloud/server/functions/buildRegistrationData.ts index 712795441ec..edc18eaf52f 100644 --- a/apps/meteor/app/cloud/server/functions/buildRegistrationData.ts +++ b/apps/meteor/app/cloud/server/functions/buildRegistrationData.ts @@ -6,12 +6,12 @@ import { Users } from '../../../models/server'; import { statistics } from '../../../statistics/server'; import { LICENSE_VERSION } from '../license'; -type WorkspaceRegistrationData = { +type WorkspaceRegistrationData = { uniqueId: string; workspaceId: SettingValue; address: SettingValue; contactName: string; - contactEmail: string; + contactEmail: T; seats: number; allowMarketing: SettingValue; accountName: SettingValue; @@ -33,7 +33,7 @@ type WorkspaceRegistrationData = { npsEnabled: SettingValue; }; -export async function buildWorkspaceRegistrationData(contactEmail: string): Promise { +export async function buildWorkspaceRegistrationData(contactEmail: T): Promise> { const stats = (await Statistics.findLast()) || (await statistics.get()); const address = settings.get('Site_Url'); diff --git a/apps/meteor/app/cloud/server/functions/connectWorkspace.js b/apps/meteor/app/cloud/server/functions/connectWorkspace.ts similarity index 85% rename from apps/meteor/app/cloud/server/functions/connectWorkspace.js rename to apps/meteor/app/cloud/server/functions/connectWorkspace.ts index 9c2a424c322..a0d76a402ef 100644 --- a/apps/meteor/app/cloud/server/functions/connectWorkspace.js +++ b/apps/meteor/app/cloud/server/functions/connectWorkspace.ts @@ -1,16 +1,16 @@ import { HTTP } from 'meteor/http'; +import { Settings } from '@rocket.chat/models'; import { getRedirectUri } from './getRedirectUri'; import { retrieveRegistrationStatus } from './retrieveRegistrationStatus'; -import { Settings } from '../../../models/server'; import { settings } from '../../../settings/server'; import { saveRegistrationData } from './saveRegistrationData'; import { SystemLogger } from '../../../../server/lib/logger/system'; -export function connectWorkspace(token) { +export async function connectWorkspace(token: string) { const { connectToCloud } = retrieveRegistrationStatus(); if (!connectToCloud) { - Settings.updateValueById('Register_Server', true); + await Settings.updateValueById('Register_Server', true); } // shouldn't get here due to checking this on the method @@ -36,7 +36,7 @@ export function connectWorkspace(token) { }, 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. Error: ${e.response.data.error}`); } else { @@ -52,7 +52,7 @@ export function connectWorkspace(token) { return false; } - Promise.await(saveRegistrationData(data)); + await saveRegistrationData(data); return true; } diff --git a/apps/meteor/app/cloud/server/functions/disconnectWorkspace.js b/apps/meteor/app/cloud/server/functions/disconnectWorkspace.ts similarity index 55% rename from apps/meteor/app/cloud/server/functions/disconnectWorkspace.js rename to apps/meteor/app/cloud/server/functions/disconnectWorkspace.ts index c1ac36729aa..0c1a9743b10 100644 --- a/apps/meteor/app/cloud/server/functions/disconnectWorkspace.js +++ b/apps/meteor/app/cloud/server/functions/disconnectWorkspace.ts @@ -1,13 +1,14 @@ +import { Settings } from '@rocket.chat/models'; + import { retrieveRegistrationStatus } from './retrieveRegistrationStatus'; -import { Settings } from '../../../models/server'; -export function disconnectWorkspace() { +export async function disconnectWorkspace() { const { connectToCloud } = retrieveRegistrationStatus(); if (!connectToCloud) { return true; } - Settings.updateValueById('Register_Server', false); + await Settings.updateValueById('Register_Server', false); return true; } diff --git a/apps/meteor/app/cloud/server/functions/getOAuthAuthorizationUrl.js b/apps/meteor/app/cloud/server/functions/getOAuthAuthorizationUrl.ts similarity index 57% rename from apps/meteor/app/cloud/server/functions/getOAuthAuthorizationUrl.js rename to apps/meteor/app/cloud/server/functions/getOAuthAuthorizationUrl.ts index 54b2df8a490..ffae4d1e5a1 100644 --- a/apps/meteor/app/cloud/server/functions/getOAuthAuthorizationUrl.js +++ b/apps/meteor/app/cloud/server/functions/getOAuthAuthorizationUrl.ts @@ -1,20 +1,20 @@ import { Random } from 'meteor/random'; +import { Settings } from '@rocket.chat/models'; import { getRedirectUri } from './getRedirectUri'; -import { Settings } from '../../../models/server'; import { settings } from '../../../settings/server'; import { userScopes } from '../oauthScopes'; -export function getOAuthAuthorizationUrl() { +export async function getOAuthAuthorizationUrl() { const state = Random.id(); - Settings.updateValueById('Cloud_Workspace_Registration_State', state); + await Settings.updateValueById('Cloud_Workspace_Registration_State', state); const cloudUrl = settings.get('Cloud_Url'); - const client_id = settings.get('Cloud_Workspace_Client_Id'); + const clientId = settings.get('Cloud_Workspace_Client_Id'); const redirectUri = getRedirectUri(); const scope = userScopes.join(' '); - return `${cloudUrl}/authorize?response_type=code&client_id=${client_id}&redirect_uri=${redirectUri}&scope=${scope}&state=${state}`; + return `${cloudUrl}/authorize?response_type=code&client_id=${clientId}&redirect_uri=${redirectUri}&scope=${scope}&state=${state}`; } diff --git a/apps/meteor/app/cloud/server/functions/getWorkspaceAccessToken.js b/apps/meteor/app/cloud/server/functions/getWorkspaceAccessToken.ts similarity index 52% rename from apps/meteor/app/cloud/server/functions/getWorkspaceAccessToken.js rename to apps/meteor/app/cloud/server/functions/getWorkspaceAccessToken.ts index b1538161caf..cea3a6192a2 100644 --- a/apps/meteor/app/cloud/server/functions/getWorkspaceAccessToken.js +++ b/apps/meteor/app/cloud/server/functions/getWorkspaceAccessToken.ts @@ -1,6 +1,7 @@ +import { Settings } from '@rocket.chat/models'; + import { retrieveRegistrationStatus } from './retrieveRegistrationStatus'; import { getWorkspaceAccessTokenWithScope } from './getWorkspaceAccessTokenWithScope'; -import { Settings } from '../../../models/server'; import { settings } from '../../../settings/server'; /** @@ -9,25 +10,31 @@ import { settings } from '../../../settings/server'; * @param {boolean} save * @returns string */ -export function getWorkspaceAccessToken(forceNew = false, scope = '', save = true) { +export async function getWorkspaceAccessToken(forceNew = false, scope = '', save = true) { const { connectToCloud, workspaceRegistered } = retrieveRegistrationStatus(); if (!connectToCloud || !workspaceRegistered) { return ''; } - const expires = Settings.findOneById('Cloud_Workspace_Access_Token_Expires_At'); + const expires = await Settings.findOneById('Cloud_Workspace_Access_Token_Expires_At'); + + if (expires === null) { + throw new Error('Cloud_Workspace_Access_Token_Expires_At is not set'); + } const now = new Date(); - if (now < expires.value && !forceNew) { + if (expires.value && now < expires.value && !forceNew) { return settings.get('Cloud_Workspace_Access_Token'); } const accessToken = getWorkspaceAccessTokenWithScope(scope); if (save) { - Settings.updateValueById('Cloud_Workspace_Access_Token', accessToken.token); - Settings.updateValueById('Cloud_Workspace_Access_Token_Expires_At', accessToken.expiresAt); + await Promise.all([ + Settings.updateValueById('Cloud_Workspace_Access_Token', accessToken.token), + Settings.updateValueById('Cloud_Workspace_Access_Token_Expires_At', accessToken.expiresAt), + ]); } return accessToken.token; diff --git a/apps/meteor/app/cloud/server/functions/getWorkspaceLicense.js b/apps/meteor/app/cloud/server/functions/getWorkspaceLicense.ts similarity index 72% rename from apps/meteor/app/cloud/server/functions/getWorkspaceLicense.js rename to apps/meteor/app/cloud/server/functions/getWorkspaceLicense.ts index 6e2e8300d2e..23cf5e3b501 100644 --- a/apps/meteor/app/cloud/server/functions/getWorkspaceLicense.js +++ b/apps/meteor/app/cloud/server/functions/getWorkspaceLicense.ts @@ -1,14 +1,14 @@ import { HTTP } from 'meteor/http'; +import { Settings } from '@rocket.chat/models'; import { getWorkspaceAccessToken } from './getWorkspaceAccessToken'; import { settings } from '../../../settings/server'; -import { Settings } from '../../../models/server'; import { callbacks } from '../../../../lib/callbacks'; import { LICENSE_VERSION } from '../license'; import { SystemLogger } from '../../../../server/lib/logger/system'; -export function getWorkspaceLicense() { - const token = getWorkspaceAccessToken(); +export async function getWorkspaceLicense() { + const token = await getWorkspaceAccessToken(); if (!token) { return { updated: false, license: '' }; @@ -21,7 +21,7 @@ export function getWorkspaceLicense() { Authorization: `Bearer ${token}`, }, }); - } catch (e) { + } catch (e: any) { if (e.response && e.response.data && e.response.data.error) { SystemLogger.error(`Failed to update license from Rocket.Chat Cloud. Error: ${e.response.data.error}`); } else { @@ -32,13 +32,17 @@ export function getWorkspaceLicense() { } const remoteLicense = licenseResult.data; - const currentLicense = settings.get('Cloud_Workspace_License'); + const currentLicense = await Settings.findOne('Cloud_Workspace_License'); + + if (!currentLicense || !currentLicense._updatedAt) { + throw new Error('Failed to retrieve current license'); + } if (remoteLicense.updatedAt <= currentLicense._updatedAt) { return { updated: false, license: '' }; } - Settings.updateValueById('Cloud_Workspace_License', remoteLicense.license); + await Settings.updateValueById('Cloud_Workspace_License', remoteLicense.license); callbacks.run('workspaceLicenseChanged', remoteLicense.license); diff --git a/apps/meteor/app/cloud/server/functions/startRegisterWorkspace.js b/apps/meteor/app/cloud/server/functions/startRegisterWorkspace.ts similarity index 81% rename from apps/meteor/app/cloud/server/functions/startRegisterWorkspace.js rename to apps/meteor/app/cloud/server/functions/startRegisterWorkspace.ts index 9633a383515..3b20e89ac97 100644 --- a/apps/meteor/app/cloud/server/functions/startRegisterWorkspace.js +++ b/apps/meteor/app/cloud/server/functions/startRegisterWorkspace.ts @@ -1,9 +1,9 @@ import { HTTP } from 'meteor/http'; +import { Settings } from '@rocket.chat/models'; import { retrieveRegistrationStatus } from './retrieveRegistrationStatus'; import { syncWorkspace } from './syncWorkspace'; import { settings } from '../../../settings/server'; -import { Settings } from '../../../models/server'; import { buildWorkspaceRegistrationData } from './buildRegistrationData'; import { SystemLogger } from '../../../../server/lib/logger/system'; @@ -15,9 +15,9 @@ export async function startRegisterWorkspace(resend = false) { return true; } - Settings.updateValueById('Register_Server', true); + await Settings.updateValueById('Register_Server', true); - const regInfo = await buildWorkspaceRegistrationData(); + const regInfo = await buildWorkspaceRegistrationData(undefined); const cloudUrl = settings.get('Cloud_Url'); @@ -26,7 +26,7 @@ export async function startRegisterWorkspace(resend = false) { result = HTTP.post(`${cloudUrl}/api/v2/register/workspace?resend=${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 { @@ -39,7 +39,7 @@ export async function startRegisterWorkspace(resend = false) { return false; } - Settings.updateValueById('Cloud_Workspace_Id', data.id); + await Settings.updateValueById('Cloud_Workspace_Id', data.id); return true; } diff --git a/apps/meteor/app/cloud/server/functions/syncWorkspace.js b/apps/meteor/app/cloud/server/functions/syncWorkspace.ts similarity index 81% rename from apps/meteor/app/cloud/server/functions/syncWorkspace.js rename to apps/meteor/app/cloud/server/functions/syncWorkspace.ts index ad8ec96faa4..b4de1a8fd66 100644 --- a/apps/meteor/app/cloud/server/functions/syncWorkspace.js +++ b/apps/meteor/app/cloud/server/functions/syncWorkspace.ts @@ -1,10 +1,10 @@ import { HTTP } from 'meteor/http'; +import { Settings } from '@rocket.chat/models'; import { buildWorkspaceRegistrationData } from './buildRegistrationData'; import { retrieveRegistrationStatus } from './retrieveRegistrationStatus'; import { getWorkspaceAccessToken } from './getWorkspaceAccessToken'; import { getWorkspaceLicense } from './getWorkspaceLicense'; -import { Settings } from '../../../models/server'; import { settings } from '../../../settings/server'; import { getAndCreateNpsSurvey } from '../../../../server/services/nps/getAndCreateNpsSurvey'; import { NPS, Banner } from '../../../../server/sdk'; @@ -16,14 +16,14 @@ export async function syncWorkspace(reconnectCheck = false) { return false; } - const info = await buildWorkspaceRegistrationData(); + const info = await buildWorkspaceRegistrationData(undefined); const workspaceUrl = settings.get('Cloud_Workspace_Registration_Client_Uri'); let result; try { - const headers = {}; - const token = getWorkspaceAccessToken(true); + const headers: Record = {}; + const token = await getWorkspaceAccessToken(true); if (token) { headers.Authorization = `Bearer ${token}`; @@ -36,8 +36,8 @@ export async function syncWorkspace(reconnectCheck = false) { headers, }); - getWorkspaceLicense(); - } catch (e) { + await getWorkspaceLicense(); + } catch (e: any) { if (e.response && e.response.data && e.response.data.error) { SystemLogger.error(`Failed to sync with Rocket.Chat Cloud. Error: ${e.response.data.error}`); } else { @@ -53,11 +53,11 @@ export async function syncWorkspace(reconnectCheck = false) { } if (data.publicKey) { - Settings.updateValueById('Cloud_Workspace_PublicKey', data.publicKey); + await Settings.updateValueById('Cloud_Workspace_PublicKey', data.publicKey); } if (data.trial?.trialId) { - Settings.updateValueById('Cloud_Workspace_Had_Trial', true); + await Settings.updateValueById('Cloud_Workspace_Had_Trial', true); } if (data.nps) { @@ -69,6 +69,10 @@ export async function syncWorkspace(reconnectCheck = false) { npsId, startAt, expireAt: new Date(expireAt), + createdBy: { + _id: 'rocket.cat', + username: 'rocket.cat', + }, }); const now = new Date(); diff --git a/apps/meteor/app/cloud/server/functions/unregisterWorkspace.js b/apps/meteor/app/cloud/server/functions/unregisterWorkspace.js deleted file mode 100644 index 37895e53e24..00000000000 --- a/apps/meteor/app/cloud/server/functions/unregisterWorkspace.js +++ /dev/null @@ -1,19 +0,0 @@ -import { retrieveRegistrationStatus } from './retrieveRegistrationStatus'; -import { Settings } from '../../../models/server'; - -export function unregisterWorkspace() { - const { workspaceRegistered } = retrieveRegistrationStatus(); - if (!workspaceRegistered) { - return true; - } - - Settings.updateValueById('Cloud_Workspace_Id', null); - Settings.updateValueById('Cloud_Workspace_Name', null); - Settings.updateValueById('Cloud_Workspace_Client_Id', null); - Settings.updateValueById('Cloud_Workspace_Client_Secret', null); - Settings.updateValueById('Cloud_Workspace_Client_Secret_Expires_At', null); - Settings.updateValueById('Cloud_Workspace_PublicKey', null); - Settings.updateValueById('Cloud_Workspace_Registration_Client_Uri', null); - - return true; -} diff --git a/apps/meteor/app/cloud/server/functions/unregisterWorkspace.ts b/apps/meteor/app/cloud/server/functions/unregisterWorkspace.ts new file mode 100644 index 00000000000..e4023b6f66f --- /dev/null +++ b/apps/meteor/app/cloud/server/functions/unregisterWorkspace.ts @@ -0,0 +1,22 @@ +import { Settings } from '@rocket.chat/models'; + +import { retrieveRegistrationStatus } from './retrieveRegistrationStatus'; + +export async function unregisterWorkspace() { + const { workspaceRegistered } = retrieveRegistrationStatus(); + if (!workspaceRegistered) { + return true; + } + + await Promise.all([ + Settings.updateValueById('Cloud_Workspace_Id', null), + Settings.updateValueById('Cloud_Workspace_Name', null), + Settings.updateValueById('Cloud_Workspace_Client_Id', null), + Settings.updateValueById('Cloud_Workspace_Client_Secret', null), + Settings.updateValueById('Cloud_Workspace_Client_Secret_Expires_At', null), + Settings.updateValueById('Cloud_Workspace_PublicKey', null), + Settings.updateValueById('Cloud_Workspace_Registration_Client_Uri', null), + ]); + + return true; +} diff --git a/apps/meteor/app/cloud/server/index.js b/apps/meteor/app/cloud/server/index.js index 55ae7bbc04c..50dc3361a22 100644 --- a/apps/meteor/app/cloud/server/index.js +++ b/apps/meteor/app/cloud/server/index.js @@ -46,7 +46,7 @@ Meteor.startup(function () { try { SystemLogger.info('REG_TOKEN Provided. Attempting to register'); - if (!connectWorkspace(process.env.REG_TOKEN)) { + if (!Promise.await(connectWorkspace(process.env.REG_TOKEN))) { throw new Error("Couldn't register with token. Please make sure token is valid or hasn't already been used"); } 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 73b7b475f89..8fd8700b4f4 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,8 @@ import { Meteor } from 'meteor/meteor'; +import { Settings } from '@rocket.chat/models'; import { settings } from '../../../settings/server'; -import { Users, Rooms, Settings } from '../../../models/server'; +import { Users, Rooms } from '../../../models/server'; import { sendMessage } from '../../../lib'; class ErrorHandler { diff --git a/apps/meteor/app/file-upload/server/lib/FileUpload.js b/apps/meteor/app/file-upload/server/lib/FileUpload.js index 7d7afd4c8dc..dc99688c564 100644 --- a/apps/meteor/app/file-upload/server/lib/FileUpload.js +++ b/apps/meteor/app/file-upload/server/lib/FileUpload.js @@ -12,12 +12,11 @@ import { Match } from 'meteor/check'; import { TAPi18n } from 'meteor/rocketchat:tap-i18n'; import filesize from 'filesize'; import { AppsEngineException } from '@rocket.chat/apps-engine/definition/exceptions'; -import { Avatars, UserDataFiles, Uploads } from '@rocket.chat/models'; +import { Avatars, UserDataFiles, Uploads, Settings } from '@rocket.chat/models'; import { settings } from '../../../settings/server'; import Users from '../../../models/server/models/Users'; import Rooms from '../../../models/server/models/Rooms'; -import Settings from '../../../models/server/models/Settings'; import { mime } from '../../../utils/lib/mimeTypes'; import { hasPermission } from '../../../authorization/server/functions/hasPermission'; import { canAccessRoom } from '../../../authorization/server/functions/canAccessRoom'; @@ -32,11 +31,11 @@ import { roomCoordinator } from '../../../../server/lib/rooms/roomCoordinator'; const cookie = new Cookies(); let maxFileSize = 0; -settings.watch('FileUpload_MaxFileSize', function (value) { +settings.watch('FileUpload_MaxFileSize', async function (value) { try { maxFileSize = parseInt(value); } catch (e) { - maxFileSize = Settings.findOneById('FileUpload_MaxFileSize').packageValue; + maxFileSize = await Settings.findOneById('FileUpload_MaxFileSize').packageValue; } }); diff --git a/apps/meteor/app/importer-csv/server/importer.js b/apps/meteor/app/importer-csv/server/importer.js index 1d2ce78e38d..61ba6824a8c 100644 --- a/apps/meteor/app/importer-csv/server/importer.js +++ b/apps/meteor/app/importer-csv/server/importer.js @@ -1,7 +1,8 @@ +import { Settings } from '@rocket.chat/models'; import { Random } from 'meteor/random'; import { Base, ProgressStep, ImporterWebsocket } from '../../importer/server'; -import { Users, Settings as SettingsRaw } from '../../models/server'; +import { Users } from '../../models/server'; export class CsvImporter extends Base { constructor(info, importRecord) { @@ -121,7 +122,7 @@ export class CsvImporter extends Base { }); } - SettingsRaw.incrementValueById('CSV_Importer_Count', usersCount); + Promise.await(Settings.incrementValueById('CSV_Importer_Count', usersCount)); super.updateRecord({ 'count.users': usersCount }); return increaseProgressCount(); } diff --git a/apps/meteor/app/importer-hipchat-enterprise/server/importer.js b/apps/meteor/app/importer-hipchat-enterprise/server/importer.js index 0e1fed9db75..86a59e058a6 100644 --- a/apps/meteor/app/importer-hipchat-enterprise/server/importer.js +++ b/apps/meteor/app/importer-hipchat-enterprise/server/importer.js @@ -3,9 +3,9 @@ import path from 'path'; import fs from 'fs'; import { Meteor } from 'meteor/meteor'; +import { Settings } from '@rocket.chat/models'; import { Base, ProgressStep } from '../../importer/server'; -import { Settings as SettingsRaw } from '../../models/server'; export class HipChatEnterpriseImporter extends Base { constructor(info, importRecord) { @@ -53,7 +53,7 @@ export class HipChatEnterpriseImporter extends Base { this.converter.addUser(newUser); } - SettingsRaw.incrementValueById('Hipchat_Enterprise_Importer_Count', count); + await Settings.incrementValueById('Hipchat_Enterprise_Importer_Count', count); super.updateRecord({ 'count.users': count }); super.addCountToTotal(count); } diff --git a/apps/meteor/app/importer-slack-users/server/importer.js b/apps/meteor/app/importer-slack-users/server/importer.js index da35a8d1bfc..83e221c836a 100644 --- a/apps/meteor/app/importer-slack-users/server/importer.js +++ b/apps/meteor/app/importer-slack-users/server/importer.js @@ -1,10 +1,11 @@ import { Meteor } from 'meteor/meteor'; import { Accounts } from 'meteor/accounts-base'; import { Random } from 'meteor/random'; +import { Settings } from '@rocket.chat/models'; import { RawImports, Base, ProgressStep, Selection, SelectionUser } from '../../importer/server'; import { RocketChatFile } from '../../file'; -import { Users, Settings as SettingsRaw } from '../../models/server'; +import { Users } from '../../models/server'; export class SlackUsersImporter extends Base { constructor(info, importRecord) { @@ -166,7 +167,7 @@ export class SlackUsersImporter extends Base { }); } - SettingsRaw.incrementValueById('Slack_Users_Importer_Count', this.users.users.length); + Settings.incrementValueById('Slack_Users_Importer_Count', this.users.users.length); super.updateProgress(ProgressStep.FINISHING); super.updateProgress(ProgressStep.DONE); } catch (e) { diff --git a/apps/meteor/app/importer-slack/server/importer.js b/apps/meteor/app/importer-slack/server/importer.js index 8a9caa1775f..99661ca06da 100644 --- a/apps/meteor/app/importer-slack/server/importer.js +++ b/apps/meteor/app/importer-slack/server/importer.js @@ -1,7 +1,8 @@ import _ from 'underscore'; +import { Settings } from '@rocket.chat/models'; import { Base, ProgressStep, ImporterWebsocket } from '../../importer/server'; -import { Messages, ImportData, Settings as SettingsRaw } from '../../models/server'; +import { Messages, ImportData } from '../../models/server'; import { settings } from '../../settings/server'; import { MentionsParser } from '../../mentions/lib/MentionsParser'; import { getUserAvatarURL } from '../../utils/lib/getUserAvatarURL'; @@ -155,7 +156,7 @@ export class SlackImporter extends Base { } this.converter.addUser(newUser); - SettingsRaw.incrementValueById('Slack_Importer_Count'); + Promise.all(Settings.incrementValueById('Slack_Importer_Count')); } } diff --git a/apps/meteor/app/importer/server/classes/ImporterBase.js b/apps/meteor/app/importer/server/classes/ImporterBase.js index 73ebfdd161a..1190b8b9051 100644 --- a/apps/meteor/app/importer/server/classes/ImporterBase.js +++ b/apps/meteor/app/importer/server/classes/ImporterBase.js @@ -2,6 +2,7 @@ import http from 'http'; import fs from 'fs'; import https from 'https'; +import { Settings } from '@rocket.chat/models'; import { Meteor } from 'meteor/meteor'; import AdmZip from 'adm-zip'; import getFileType from 'file-type'; @@ -11,7 +12,7 @@ import { ImporterWebsocket } from './ImporterWebsocket'; import { ProgressStep } from '../../lib/ImporterProgressStep'; import { ImporterInfo } from '../../lib/ImporterInfo'; import { RawImports } from '../models/RawImports'; -import { Settings, Imports, ImportData } from '../../../models/server'; +import { Imports, ImportData } from '../../../models/server'; import { Logger } from '../../../logger'; import { ImportDataConverter } from './ImportDataConverter'; import { t } from '../../../utils/server'; @@ -250,29 +251,29 @@ export class Base { switch (step) { case ProgressStep.IMPORTING_STARTED: - this.oldSettings.Accounts_AllowedDomainsList = Settings.findOneById('Accounts_AllowedDomainsList').value; - Settings.updateValueById('Accounts_AllowedDomainsList', ''); + this.oldSettings.Accounts_AllowedDomainsList = Promise.await(Settings.findOneById('Accounts_AllowedDomainsList')).value; + Promise.await(Settings.updateValueById('Accounts_AllowedDomainsList', '')); - this.oldSettings.Accounts_AllowUsernameChange = Settings.findOneById('Accounts_AllowUsernameChange').value; - Settings.updateValueById('Accounts_AllowUsernameChange', true); + this.oldSettings.Accounts_AllowUsernameChange = Promise.await(Settings.findOneById('Accounts_AllowUsernameChange')).value; + Promise.await(Settings.updateValueById('Accounts_AllowUsernameChange', true)); - this.oldSettings.FileUpload_MaxFileSize = Settings.findOneById('FileUpload_MaxFileSize').value; - Settings.updateValueById('FileUpload_MaxFileSize', -1); + this.oldSettings.FileUpload_MaxFileSize = Promise.await(Settings.findOneById('FileUpload_MaxFileSize')).value; + Promise.await(Settings.updateValueById('FileUpload_MaxFileSize', -1)); - this.oldSettings.FileUpload_MediaTypeWhiteList = Settings.findOneById('FileUpload_MediaTypeWhiteList').value; - Settings.updateValueById('FileUpload_MediaTypeWhiteList', '*'); + this.oldSettings.FileUpload_MediaTypeWhiteList = Promise.await(Settings.findOneById('FileUpload_MediaTypeWhiteList')).value; + Promise.await(Settings.updateValueById('FileUpload_MediaTypeWhiteList', '*')); - this.oldSettings.FileUpload_MediaTypeBlackList = Settings.findOneById('FileUpload_MediaTypeBlackList').value; - Settings.updateValueById('FileUpload_MediaTypeBlackList', ''); + this.oldSettings.FileUpload_MediaTypeBlackList = Promise.await(Settings.findOneById('FileUpload_MediaTypeBlackList')).value; + Promise.await(Settings.updateValueById('FileUpload_MediaTypeBlackList', '')); break; case ProgressStep.DONE: case ProgressStep.ERROR: case ProgressStep.CANCELLED: - Settings.updateValueById('Accounts_AllowedDomainsList', this.oldSettings.Accounts_AllowedDomainsList); - Settings.updateValueById('Accounts_AllowUsernameChange', this.oldSettings.Accounts_AllowUsernameChange); - Settings.updateValueById('FileUpload_MaxFileSize', this.oldSettings.FileUpload_MaxFileSize); - Settings.updateValueById('FileUpload_MediaTypeWhiteList', this.oldSettings.FileUpload_MediaTypeWhiteList); - Settings.updateValueById('FileUpload_MediaTypeBlackList', this.oldSettings.FileUpload_MediaTypeBlackList); + Promise.await(Settings.updateValueById('Accounts_AllowedDomainsList', this.oldSettings.Accounts_AllowedDomainsList)); + Promise.await(Settings.updateValueById('Accounts_AllowUsernameChange', this.oldSettings.Accounts_AllowUsernameChange)); + Promise.await(Settings.updateValueById('FileUpload_MaxFileSize', this.oldSettings.FileUpload_MaxFileSize)); + Promise.await(Settings.updateValueById('FileUpload_MediaTypeWhiteList', this.oldSettings.FileUpload_MediaTypeWhiteList)); + Promise.await(Settings.updateValueById('FileUpload_MediaTypeBlackList', this.oldSettings.FileUpload_MediaTypeBlackList)); break; } diff --git a/apps/meteor/app/irc/server/irc-bridge/index.js b/apps/meteor/app/irc/server/irc-bridge/index.js index 04054f027bc..855f68acdde 100644 --- a/apps/meteor/app/irc/server/irc-bridge/index.js +++ b/apps/meteor/app/irc/server/irc-bridge/index.js @@ -2,12 +2,12 @@ import { Meteor } from 'meteor/meteor'; import Queue from 'queue-fifo'; import moment from 'moment'; import _ from 'underscore'; +import { Settings } from '@rocket.chat/models'; import * as peerCommandHandlers from './peerHandlers'; import * as localCommandHandlers from './localHandlers'; import { callbacks } from '../../../../lib/callbacks'; import * as servers from '../servers'; -import { Settings } from '../../../models/server'; import { Logger } from '../../../logger/server'; const logger = new Logger('IRC Bridge'); @@ -57,7 +57,7 @@ class Bridge { removed = false; this.loggedInUsers = []; - const lastPing = Settings.findOneById('IRC_Bridge_Last_Ping'); + const lastPing = Promise.await(Settings.findOneById('IRC_Bridge_Last_Ping')); if (lastPing) { if (Math.abs(moment(lastPing.value).diff()) < 1000 * 30) { this.log('Not trying to connect.'); diff --git a/apps/meteor/app/irc/server/methods/resetIrcConnection.js b/apps/meteor/app/irc/server/methods/resetIrcConnection.ts similarity index 75% rename from apps/meteor/app/irc/server/methods/resetIrcConnection.js rename to apps/meteor/app/irc/server/methods/resetIrcConnection.ts index 9c033c27017..c72f894b64b 100644 --- a/apps/meteor/app/irc/server/methods/resetIrcConnection.js +++ b/apps/meteor/app/irc/server/methods/resetIrcConnection.ts @@ -1,27 +1,33 @@ +import { Settings } from '@rocket.chat/models'; import { Meteor } from 'meteor/meteor'; -import { Settings } from '../../../models/server'; import { settings } from '../../../settings/server'; import Bridge from '../irc-bridge'; Meteor.methods({ resetIrcConnection() { const ircEnabled = Boolean(settings.get('IRC_Enabled')); - Settings.upsert( + Settings.updateOne( { _id: 'IRC_Bridge_Last_Ping' }, { $set: { value: new Date(0), }, }, + { + upsert: true, + }, ); - Settings.upsert( + Settings.updateOne( { _id: 'IRC_Bridge_Reset_Time' }, { $set: { value: new Date(), }, }, + { + upsert: true, + }, ); if (!ircEnabled) { @@ -47,7 +53,7 @@ Meteor.methods({ peer: settings.get('IRC_Peer_Password'), }, }; - + // TODO: is this the best way to do this? is this really necessary? Meteor.ircBridge = new Bridge(config); Meteor.ircBridge.init(); }), @@ -60,3 +66,10 @@ Meteor.methods({ }; }, }); + +declare module 'meteor/meteor' { + // eslint-disable-next-line @typescript-eslint/no-namespace + export namespace Meteor { + export let ircBridge: Bridge; + } +} diff --git a/apps/meteor/app/lib/server/methods/sendInvitationEmail.js b/apps/meteor/app/lib/server/methods/sendInvitationEmail.ts similarity index 71% rename from apps/meteor/app/lib/server/methods/sendInvitationEmail.js rename to apps/meteor/app/lib/server/methods/sendInvitationEmail.ts index fe33f73fe32..23f575a96c0 100644 --- a/apps/meteor/app/lib/server/methods/sendInvitationEmail.js +++ b/apps/meteor/app/lib/server/methods/sendInvitationEmail.ts @@ -1,10 +1,10 @@ import { Meteor } from 'meteor/meteor'; import { check } from 'meteor/check'; +import { Settings } from '@rocket.chat/models'; import * as Mailer from '../../../mailer'; -import { hasPermission } from '../../../authorization'; +import { hasPermission } from '../../../authorization/server'; import { settings } from '../../../settings/server'; -import { Settings as SettingsRaw } from '../../../models/server'; let html = ''; Meteor.startup(() => { @@ -14,14 +14,15 @@ Meteor.startup(() => { }); Meteor.methods({ - sendInvitationEmail(emails) { + async sendInvitationEmail(emails) { check(emails, [String]); - if (!Meteor.userId()) { + const uid = Meteor.userId(); + if (!uid) { throw new Meteor.Error('error-invalid-user', 'Invalid user', { method: 'sendInvitationEmail', }); } - if (!hasPermission(Meteor.userId(), 'bulk-register-user')) { + if (!hasPermission(uid, 'bulk-register-user')) { throw new Meteor.Error('error-not-allowed', 'Not allowed', { method: 'sendInvitationEmail', }); @@ -34,7 +35,13 @@ Meteor.methods({ }); } - const subject = settings.get('Invitation_Subject'); + const subject = settings.get('Invitation_Subject'); + + if (!subject) { + throw new Meteor.Error('error-email-send-failed', 'No subject', { + method: 'sendInvitationEmail', + }); + } return validEmails.filter((email) => { try { @@ -48,7 +55,7 @@ Meteor.methods({ }, }); - SettingsRaw.incrementValueById('Invitation_Email_Count'); + Settings.incrementValueById('Invitation_Email_Count'); return mailerResult; } catch ({ message }) { throw new Meteor.Error('error-email-send-failed', `Error trying to send email: ${message}`, { diff --git a/apps/meteor/app/livechat/imports/server/rest/upload.js b/apps/meteor/app/livechat/imports/server/rest/upload.js index 5057c5e7e00..079daec7be2 100644 --- a/apps/meteor/app/livechat/imports/server/rest/upload.js +++ b/apps/meteor/app/livechat/imports/server/rest/upload.js @@ -1,21 +1,21 @@ import { Meteor } from 'meteor/meteor'; import filesize from 'filesize'; -import { LivechatVisitors } from '@rocket.chat/models'; +import { LivechatVisitors, Settings } from '@rocket.chat/models'; import { settings } from '../../../../settings/server'; -import { Settings, LivechatRooms } from '../../../../models/server'; +import { LivechatRooms } from '../../../../models/server'; import { fileUploadIsValidContentType } from '../../../../utils/server'; -import { FileUpload } from '../../../../file-upload'; +import { FileUpload } from '../../../../file-upload/server'; import { API } from '../../../../api/server'; import { getUploadFormData } from '../../../../api/server/lib/getUploadFormData'; let maxFileSize; -settings.watch('FileUpload_MaxFileSize', function (value) { +settings.watch('FileUpload_MaxFileSize', async function (value) { try { maxFileSize = parseInt(value); } catch (e) { - maxFileSize = Settings.findOneById('FileUpload_MaxFileSize').packageValue; + maxFileSize = await Settings.findOneById('FileUpload_MaxFileSize').packageValue; } }); diff --git a/apps/meteor/app/livechat/server/api/v1/videoCall.js b/apps/meteor/app/livechat/server/api/v1/videoCall.js index ca13d3278bc..5365da0aab5 100644 --- a/apps/meteor/app/livechat/server/api/v1/videoCall.js +++ b/apps/meteor/app/livechat/server/api/v1/videoCall.js @@ -1,8 +1,9 @@ import { Meteor } from 'meteor/meteor'; import { Match, check } from 'meteor/check'; import { TAPi18n } from 'meteor/rocketchat:tap-i18n'; +import { Settings } from '@rocket.chat/models'; -import { Messages, Rooms, Settings } from '../../../../models'; +import { Messages, Rooms } from '../../../../models'; import { settings as rcSettings } from '../../../../settings/server'; import { API } from '../../../../api/server'; import { settings } from '../lib/livechat'; @@ -16,7 +17,7 @@ API.v1.addRoute( 'livechat/webrtc.call', { authRequired: true }, { - get() { + async get() { try { check(this.queryParams, { rid: Match.Maybe(String), @@ -40,7 +41,7 @@ API.v1.addRoute( throw new Meteor.Error('webRTC calling not enabled'); } - const config = Promise.await(settings()); + const config = await settings(); if (!config.theme || !config.theme.actionLinks || !config.theme.actionLinks.webrtc) { throw new Meteor.Error('invalid-livechat-config'); } @@ -48,19 +49,17 @@ API.v1.addRoute( let { callStatus } = room; if (!callStatus || callStatus === 'ended' || callStatus === 'declined') { - Settings.incrementValueById('WebRTC_Calls_Count'); + await Settings.incrementValueById('WebRTC_Calls_Count'); callStatus = 'ringing'; - Promise.await(Rooms.setCallStatusAndCallStartTime(room._id, callStatus)); - Promise.await( - Messages.createWithTypeRoomIdMessageAndUser( - 'livechat_webrtc_video_call', - room._id, - TAPi18n.__('Join_my_room_to_start_the_video_call'), - this.user, - { - actionLinks: config.theme.actionLinks.webrtc, - }, - ), + await Rooms.setCallStatusAndCallStartTime(room._id, callStatus); + await Messages.createWithTypeRoomIdMessageAndUser( + 'livechat_webrtc_video_call', + room._id, + TAPi18n.__('Join_my_room_to_start_the_video_call'), + this.user, + { + actionLinks: config.theme.actionLinks.webrtc, + }, ); } const videoCall = { diff --git a/apps/meteor/app/livechat/server/lib/Livechat.js b/apps/meteor/app/livechat/server/lib/Livechat.js index 79c0e5c86bb..b237c5ea746 100644 --- a/apps/meteor/app/livechat/server/lib/Livechat.js +++ b/apps/meteor/app/livechat/server/lib/Livechat.js @@ -9,7 +9,7 @@ import _ from 'underscore'; import s from 'underscore.string'; import moment from 'moment-timezone'; import UAParser from 'ua-parser-js'; -import { Users as UsersRaw, LivechatVisitors } from '@rocket.chat/models'; +import { Users as UsersRaw, LivechatVisitors, Settings } from '@rocket.chat/models'; import { QueueManager } from './QueueManager'; import { RoutingManager } from './RoutingManager'; @@ -22,7 +22,6 @@ import { LivechatRooms, Messages, Subscriptions, - Settings, Rooms, LivechatDepartmentAgents, LivechatDepartment, @@ -538,39 +537,41 @@ export const Livechat = { getInitSettings() { const rcSettings = {}; - Settings.findNotHiddenPublic([ - 'Livechat_title', - 'Livechat_title_color', - 'Livechat_enable_message_character_limit', - 'Livechat_message_character_limit', - 'Message_MaxAllowedSize', - 'Livechat_enabled', - 'Livechat_registration_form', - 'Livechat_allow_switching_departments', - 'Livechat_offline_title', - 'Livechat_offline_title_color', - 'Livechat_offline_message', - 'Livechat_offline_success_message', - 'Livechat_offline_form_unavailable', - 'Livechat_display_offline_form', - 'Omnichannel_call_provider', - 'Language', - 'Livechat_enable_transcript', - 'Livechat_transcript_message', - 'Livechat_fileupload_enabled', - 'FileUpload_Enabled', - 'Livechat_conversation_finished_message', - 'Livechat_conversation_finished_text', - 'Livechat_name_field_registration_form', - 'Livechat_email_field_registration_form', - 'Livechat_registration_form_message', - 'Livechat_force_accept_data_processing_consent', - 'Livechat_data_processing_consent_text', - 'Livechat_show_agent_info', - 'Livechat_clear_local_storage_when_chat_ended', - ]).forEach((setting) => { - rcSettings[setting._id] = setting.value; - }); + Promise.await( + Settings.findNotHiddenPublic([ + 'Livechat_title', + 'Livechat_title_color', + 'Livechat_enable_message_character_limit', + 'Livechat_message_character_limit', + 'Message_MaxAllowedSize', + 'Livechat_enabled', + 'Livechat_registration_form', + 'Livechat_allow_switching_departments', + 'Livechat_offline_title', + 'Livechat_offline_title_color', + 'Livechat_offline_message', + 'Livechat_offline_success_message', + 'Livechat_offline_form_unavailable', + 'Livechat_display_offline_form', + 'Omnichannel_call_provider', + 'Language', + 'Livechat_enable_transcript', + 'Livechat_transcript_message', + 'Livechat_fileupload_enabled', + 'FileUpload_Enabled', + 'Livechat_conversation_finished_message', + 'Livechat_conversation_finished_text', + 'Livechat_name_field_registration_form', + 'Livechat_email_field_registration_form', + 'Livechat_registration_form_message', + 'Livechat_force_accept_data_processing_consent', + 'Livechat_data_processing_consent_text', + 'Livechat_show_agent_info', + 'Livechat_clear_local_storage_when_chat_ended', + ]).forEach((setting) => { + rcSettings[setting._id] = setting.value; + }), + ); rcSettings.Livechat_history_monitor_type = settings.get('Livechat_history_monitor_type'); diff --git a/apps/meteor/app/livechat/server/methods/facebook.js b/apps/meteor/app/livechat/server/methods/facebook.js index 86cd7132cc1..40c7f24e52f 100644 --- a/apps/meteor/app/livechat/server/methods/facebook.js +++ b/apps/meteor/app/livechat/server/methods/facebook.js @@ -1,10 +1,10 @@ import { Meteor } from 'meteor/meteor'; +import { Settings } from '@rocket.chat/models'; import { hasPermission } from '../../../authorization'; import { SystemLogger } from '../../../../server/lib/logger/system'; import { settings } from '../../../settings/server'; import OmniChannel from '../lib/OmniChannel'; -import { Settings } from '../../../models/server'; Meteor.methods({ 'livechat:facebook'(options) { diff --git a/apps/meteor/app/livechat/server/methods/saveAppearance.js b/apps/meteor/app/livechat/server/methods/saveAppearance.ts similarity index 72% rename from apps/meteor/app/livechat/server/methods/saveAppearance.js rename to apps/meteor/app/livechat/server/methods/saveAppearance.ts index 1189a06dc6a..e1ee2b99137 100644 --- a/apps/meteor/app/livechat/server/methods/saveAppearance.js +++ b/apps/meteor/app/livechat/server/methods/saveAppearance.ts @@ -1,11 +1,12 @@ import { Meteor } from 'meteor/meteor'; +import { Settings } from '@rocket.chat/models'; -import { hasPermission } from '../../../authorization'; -import { Settings } from '../../../models/server'; +import { hasPermission } from '../../../authorization/server'; Meteor.methods({ - 'livechat:saveAppearance'(settings) { - if (!Meteor.userId() || !hasPermission(Meteor.userId(), 'view-livechat-manager')) { + async 'livechat:saveAppearance'(settings: { _id: string; value: any }[]) { + const uid = Meteor.userId(); + if (!uid || !hasPermission(uid, 'view-livechat-manager')) { throw new Meteor.Error('error-not-allowed', 'Not allowed', { method: 'livechat:saveAppearance', }); @@ -39,8 +40,10 @@ Meteor.methods({ throw new Meteor.Error('invalid-setting'); } - settings.forEach((setting) => { - Settings.updateValueById(setting._id, setting.value); - }); + await Promise.all( + settings.map((setting) => { + return Settings.updateValueById(setting._id, setting.value); + }), + ); }, }); diff --git a/apps/meteor/app/livechat/server/methods/saveIntegration.js b/apps/meteor/app/livechat/server/methods/saveIntegration.js deleted file mode 100644 index 539ff204094..00000000000 --- a/apps/meteor/app/livechat/server/methods/saveIntegration.js +++ /dev/null @@ -1,55 +0,0 @@ -import { Meteor } from 'meteor/meteor'; -import s from 'underscore.string'; - -import { hasPermission } from '../../../authorization'; -import { Settings } from '../../../models/server'; - -Meteor.methods({ - 'livechat:saveIntegration'(values) { - if (!Meteor.userId() || !hasPermission(Meteor.userId(), 'view-livechat-manager')) { - throw new Meteor.Error('error-not-allowed', 'Not allowed', { - method: 'livechat:saveIntegration', - }); - } - - if (typeof values.Livechat_webhookUrl !== 'undefined') { - Settings.updateValueById('Livechat_webhookUrl', s.trim(values.Livechat_webhookUrl)); - } - - if (typeof values.Livechat_secret_token !== 'undefined') { - Settings.updateValueById('Livechat_secret_token', s.trim(values.Livechat_secret_token)); - } - - if (typeof values.Livechat_webhook_on_start !== 'undefined') { - Settings.updateValueById('Livechat_webhook_on_start', !!values.Livechat_webhook_on_start); - } - - if (typeof values.Livechat_webhook_on_close !== 'undefined') { - Settings.updateValueById('Livechat_webhook_on_close', !!values.Livechat_webhook_on_close); - } - - if (typeof values.Livechat_webhook_on_chat_taken !== 'undefined') { - Settings.updateValueById('Livechat_webhook_on_chat_taken', !!values.Livechat_webhook_on_chat_taken); - } - - if (typeof values.Livechat_webhook_on_chat_queued !== 'undefined') { - Settings.updateValueById('Livechat_webhook_on_chat_queued', !!values.Livechat_webhook_on_chat_queued); - } - - if (typeof values.Livechat_webhook_on_forward !== 'undefined') { - Settings.updateValueById('Livechat_webhook_on_forward', !!values.Livechat_webhook_on_forward); - } - - if (typeof values.Livechat_webhook_on_offline_msg !== 'undefined') { - Settings.updateValueById('Livechat_webhook_on_offline_msg', !!values.Livechat_webhook_on_offline_msg); - } - - if (typeof values.Livechat_webhook_on_visitor_message !== 'undefined') { - Settings.updateValueById('Livechat_webhook_on_visitor_message', !!values.Livechat_webhook_on_visitor_message); - } - - if (typeof values.Livechat_webhook_on_agent_message !== 'undefined') { - Settings.updateValueById('Livechat_webhook_on_agent_message', !!values.Livechat_webhook_on_agent_message); - } - }, -}); diff --git a/apps/meteor/app/livechat/server/methods/saveIntegration.ts b/apps/meteor/app/livechat/server/methods/saveIntegration.ts new file mode 100644 index 00000000000..7422ba36252 --- /dev/null +++ b/apps/meteor/app/livechat/server/methods/saveIntegration.ts @@ -0,0 +1,56 @@ +import { Settings } from '@rocket.chat/models'; +import { Meteor } from 'meteor/meteor'; +import s from 'underscore.string'; + +import { hasPermission } from '../../../authorization/server'; + +Meteor.methods({ + async 'livechat:saveIntegration'(values) { + const uid = Meteor.userId(); + if (!uid || !hasPermission(uid, 'view-livechat-manager')) { + throw new Meteor.Error('error-not-allowed', 'Not allowed', { + method: 'livechat:saveIntegration', + }); + } + + if (typeof values.Livechat_webhookUrl !== 'undefined') { + await Settings.updateValueById('Livechat_webhookUrl', s.trim(values.Livechat_webhookUrl)); + } + + if (typeof values.Livechat_secret_token !== 'undefined') { + await Settings.updateValueById('Livechat_secret_token', s.trim(values.Livechat_secret_token)); + } + + if (typeof values.Livechat_webhook_on_start !== 'undefined') { + await Settings.updateValueById('Livechat_webhook_on_start', !!values.Livechat_webhook_on_start); + } + + if (typeof values.Livechat_webhook_on_close !== 'undefined') { + await Settings.updateValueById('Livechat_webhook_on_close', !!values.Livechat_webhook_on_close); + } + + if (typeof values.Livechat_webhook_on_chat_taken !== 'undefined') { + await Settings.updateValueById('Livechat_webhook_on_chat_taken', !!values.Livechat_webhook_on_chat_taken); + } + + if (typeof values.Livechat_webhook_on_chat_queued !== 'undefined') { + await Settings.updateValueById('Livechat_webhook_on_chat_queued', !!values.Livechat_webhook_on_chat_queued); + } + + if (typeof values.Livechat_webhook_on_forward !== 'undefined') { + await Settings.updateValueById('Livechat_webhook_on_forward', !!values.Livechat_webhook_on_forward); + } + + if (typeof values.Livechat_webhook_on_offline_msg !== 'undefined') { + await Settings.updateValueById('Livechat_webhook_on_offline_msg', !!values.Livechat_webhook_on_offline_msg); + } + + if (typeof values.Livechat_webhook_on_visitor_message !== 'undefined') { + await Settings.updateValueById('Livechat_webhook_on_visitor_message', !!values.Livechat_webhook_on_visitor_message); + } + + if (typeof values.Livechat_webhook_on_agent_message !== 'undefined') { + await Settings.updateValueById('Livechat_webhook_on_agent_message', !!values.Livechat_webhook_on_agent_message); + } + }, +}); diff --git a/apps/meteor/app/mailer/server/api.ts b/apps/meteor/app/mailer/server/api.ts index 75b5f7e1177..6f9bb6e7d66 100644 --- a/apps/meteor/app/mailer/server/api.ts +++ b/apps/meteor/app/mailer/server/api.ts @@ -7,9 +7,9 @@ import juice from 'juice'; import stripHtml from 'string-strip-html'; import { escapeHTML } from '@rocket.chat/string-helpers'; import type { ISetting } from '@rocket.chat/core-typings'; +import { Settings } from '@rocket.chat/models'; import { settings } from '../../settings/server'; -import { Settings as SettingsRaw } from '../../models/server'; import { replaceVariables } from './replaceVariables'; import { Apps } from '../../apps/server'; import { validateEmail } from '../../../lib/emailValidator'; @@ -165,7 +165,7 @@ export const sendNoWrap = ({ html = undefined; } - SettingsRaw.incrementValueById('Triggered_Emails_Count'); + Settings.incrementValueById('Triggered_Emails_Count'); const email = { to, from, replyTo, subject, html, text, headers }; diff --git a/apps/meteor/app/models/server/index.ts b/apps/meteor/app/models/server/index.ts index c378f5ad671..4620f9d4caa 100644 --- a/apps/meteor/app/models/server/index.ts +++ b/apps/meteor/app/models/server/index.ts @@ -2,7 +2,6 @@ import { Base } from './models/_Base'; import { BaseDb } from './models/_BaseDb'; import Messages from './models/Messages'; import Rooms from './models/Rooms'; -import Settings from './models/Settings'; import Subscriptions from './models/Subscriptions'; import Users from './models/Users'; import Imports from './models/Imports'; @@ -25,7 +24,6 @@ export { BaseDb, Messages, Rooms, - Settings, Subscriptions, Users, Imports, diff --git a/apps/meteor/app/models/server/models/Settings.js b/apps/meteor/app/models/server/models/Settings.js deleted file mode 100644 index 490b4b2822e..00000000000 --- a/apps/meteor/app/models/server/models/Settings.js +++ /dev/null @@ -1,280 +0,0 @@ -import { Base } from './_Base'; - -export class Settings extends Base { - constructor(...args) { - super(...args); - - this.tryEnsureIndex({ blocked: 1 }, { sparse: 1 }); - this.tryEnsureIndex({ hidden: 1 }, { sparse: 1 }); - } - - // FIND - findById(_id) { - const query = { _id }; - - return this.find(query); - } - - findOneNotHiddenById(_id) { - const query = { - _id, - hidden: { $ne: true }, - }; - - return this.findOne(query); - } - - findByIds(_id = []) { - _id = [].concat(_id); - - const query = { - _id: { - $in: _id, - }, - }; - - return this.find(query); - } - - findPublic(options) { - const query = { public: true }; - - return this.find(query, options); - } - - findNotHiddenPublic(ids = []) { - const filter = { - hidden: { $ne: true }, - public: true, - }; - - if (ids.length > 0) { - filter._id = { $in: ids }; - } - - return this.find(filter, { - fields: { - _id: 1, - value: 1, - editor: 1, - enterprise: 1, - invalidValue: 1, - modules: 1, - requiredOnWizard: 1, - }, - }); - } - - findNotHiddenPublicUpdatedAfter(updatedAt) { - const filter = { - hidden: { $ne: true }, - public: true, - _updatedAt: { - $gt: updatedAt, - }, - }; - - return this.find(filter, { - fields: { - _id: 1, - value: 1, - editor: 1, - enterprise: 1, - invalidValue: 1, - modules: 1, - requiredOnWizard: 1, - }, - }); - } - - findNotHiddenPrivate() { - return this.find({ - hidden: { $ne: true }, - public: { $ne: true }, - }); - } - - findNotHidden({ updatedAfter, ...options } = {}) { - const query = { - hidden: { $ne: true }, - }; - - if (updatedAfter) { - query._updatedAt = { $gt: updatedAfter }; - } - - return this.find(query, options); - } - - findNotHiddenUpdatedAfter(updatedAt) { - return this.find({ - hidden: { $ne: true }, - _updatedAt: { - $gt: updatedAt, - }, - }); - } - - findSetupWizardSettings() { - return this.find({ wizard: { $exists: true, $ne: null } }); - } - - findEnterpriseSettings() { - return this.find({ enterprise: true }); - } - - // UPDATE - updateValueById(_id, value) { - const query = { - blocked: { $ne: true }, - value: { $ne: value }, - _id, - }; - - const update = { - $set: { - value, - }, - }; - - return this.update(query, update); - } - - incrementValueById(_id, value = 1) { - const query = { - blocked: { $ne: true }, - _id, - }; - - const update = { - $inc: { - value, - }, - }; - - return this.update(query, update); - } - - updateValueAndEditorById(_id, value, editor) { - const query = { - blocked: { $ne: true }, - value: { $ne: value }, - _id, - }; - - const update = { - $set: { - value, - editor, - }, - }; - - return this.update(query, update); - } - - updateValueNotHiddenById(_id, value) { - const query = { - _id, - hidden: { $ne: true }, - blocked: { $ne: true }, - }; - - const update = { - $set: { - value, - }, - }; - - return this.update(query, update); - } - - updateOptionsById(_id, options) { - const query = { - blocked: { $ne: true }, - _id, - }; - - const update = { $set: options }; - - return this.update(query, update); - } - - addOptionValueById(_id, option = {}) { - const query = { - blocked: { $ne: true }, - _id, - }; - - const { key, i18nLabel } = option; - const update = { - $addToSet: { - values: { - key, - i18nLabel, - }, - }, - }; - - return this.update(query, update); - } - - removeOptionValueByIdAndKey(_id, key) { - const query = { - blocked: { $ne: true }, - _id, - }; - - const update = { - $pull: { - values: { key }, - }, - }; - - return this.update(query, update); - } - - // INSERT - createWithIdAndValue(_id, value) { - const record = { - _id, - value, - _createdAt: new Date(), - }; - - return this.insert(record); - } - - // REMOVE - removeById(_id) { - const query = { - blocked: { $ne: true }, - _id, - }; - - return this.remove(query); - } - - // RENAME SETTING - renameSetting(oldId, newId) { - const oldSetting = this.findById(oldId).fetch()[0]; - if (oldSetting) { - this.removeById(oldSetting._id); - // there has been some problem with upsert() when changing the complete doc, so decide explicitly for insert or update - let newSetting = this.findById(newId).fetch()[0]; - if (newSetting) { - this.updateValueById(newId, oldSetting.value); - } else { - newSetting = oldSetting; - newSetting._id = newId; - delete newSetting.$loki; - this.insert(newSetting); - } - } - } - - insert(record, ...args) { - return super.insert({ createdAt: new Date(), ...record }, ...args); - } -} - -export default new Settings('settings', true); diff --git a/apps/meteor/app/settings/server/CachedSettings.ts b/apps/meteor/app/settings/server/CachedSettings.ts index 542768af132..d9657378151 100644 --- a/apps/meteor/app/settings/server/CachedSettings.ts +++ b/apps/meteor/app/settings/server/CachedSettings.ts @@ -13,7 +13,10 @@ type SettingsConfig = { type OverCustomSettingsConfig = Partial; export interface ICachedSettings { - initilized(): void; + /* + * @description: The settings object as ready + */ + initialized(): void; has(_id: ISetting['_id']): boolean; @@ -89,13 +92,13 @@ export class CachedSettings /** * The settings object as ready */ - initilized(): void { + initialized(): void { if (this.ready) { return; } this.ready = true; this.emit('ready'); - SystemLogger.debug('Settings initalized'); + SystemLogger.debug('Settings initialized'); } /** diff --git a/apps/meteor/app/settings/server/SettingsRegistry.ts b/apps/meteor/app/settings/server/SettingsRegistry.ts index 0c5c00ff2c1..71ae504a68b 100644 --- a/apps/meteor/app/settings/server/SettingsRegistry.ts +++ b/apps/meteor/app/settings/server/SettingsRegistry.ts @@ -1,8 +1,9 @@ import { Emitter } from '@rocket.chat/emitter'; import { isEqual } from 'underscore'; import { ISetting, ISettingGroup, isSettingEnterprise, SettingValue } from '@rocket.chat/core-typings'; +import { Settings } from '@rocket.chat/models'; +import type { ISettingsModel } from '@rocket.chat/model-typings'; -import type SettingsModel from '../../models/server/models/Settings'; import { SystemLogger } from '../../../server/lib/logger/system'; import { overwriteSetting } from './functions/overwriteSetting'; import { overrideSetting } from './functions/overrideSetting'; @@ -83,13 +84,13 @@ const compareSettings = compareSettingsIgnoringKeys([ ]); export class SettingsRegistry { - private model: typeof SettingsModel; + private model: ISettingsModel; private store: ICachedSettings; private _sorter: { [key: string]: number } = {}; - constructor({ store, model }: { store: ICachedSettings; model: typeof SettingsModel }) { + constructor({ store, model }: { store: ICachedSettings; model: typeof Settings }) { this.store = store; this.model = model; } @@ -97,7 +98,7 @@ export class SettingsRegistry { /* * Add a setting */ - add(_id: string, value: SettingValue, { sorter, section, group, ...options }: ISettingAddOptions = {}): void { + async add(_id: string, value: SettingValue, { sorter, section, group, ...options }: ISettingAddOptions = {}): Promise { if (!_id || value == null) { throw new Error('Invalid arguments'); } @@ -156,7 +157,7 @@ export class SettingsRegistry { const overwrittenKeys = Object.keys(settingFromCodeOverwritten); const removedKeys = Object.keys(settingStored).filter((key) => !['_updatedAt'].includes(key) && !overwrittenKeys.includes(key)); - this.model.upsert( + await this.model.updateOne( { _id }, { $set: { ...settingOverwrittenProps }, @@ -164,6 +165,7 @@ export class SettingsRegistry { $unset: removedKeys.reduce((unset, key) => ({ ...unset, [key]: 1 }), {}), }), }, + { upsert: true }, ); return; @@ -171,7 +173,7 @@ export class SettingsRegistry { if (settingStored && isOverwritten) { if (settingStored.value !== settingFromCodeOverwritten.value) { - this.model.upsert({ _id }, settingProps); + await this.model.updateOne({ _id }, settingProps, { upsert: true }); } return; } @@ -189,7 +191,7 @@ export class SettingsRegistry { const setting = isOverwritten ? settingFromCodeOverwritten : settingOverwrittenDefault; - this.model.insert(setting); // no need to emit unless we remove the oplog + await this.model.insertOne(setting); // no need to emit unless we remove the oplog this.store.set(setting); } @@ -197,10 +199,10 @@ export class SettingsRegistry { /* * Add a setting group */ - addGroup(_id: string, cb: addGroupCallback): void; + async addGroup(_id: string, cb: addGroupCallback): Promise; // eslint-disable-next-line no-dupe-class-members - addGroup(_id: string, groupOptions: ISettingAddGroupOptions | addGroupCallback = {}, cb?: addGroupCallback): void { + async addGroup(_id: string, groupOptions: ISettingAddGroupOptions | addGroupCallback = {}, cb?: addGroupCallback): Promise { if (!_id || (groupOptions instanceof Function && cb)) { throw new Error('Invalid arguments'); } @@ -214,7 +216,7 @@ export class SettingsRegistry { if (!this.store.has(_id)) { options.ts = new Date(); - this.model.insert(options); + await this.model.insertOne(options as ISetting); this.store.set(options as ISetting); } diff --git a/apps/meteor/app/settings/server/functions/settings.mocks.ts b/apps/meteor/app/settings/server/functions/settings.mocks.ts index e85f3f8a2bd..59c47715d80 100644 --- a/apps/meteor/app/settings/server/functions/settings.mocks.ts +++ b/apps/meteor/app/settings/server/functions/settings.mocks.ts @@ -34,14 +34,14 @@ class SettingsClass { return [...this.data.values()].find((data) => Object.entries(query).every(([key, value]) => this.checkQueryMatch(key, data, value))); } - insert(doc: any): void { + insertOne(doc: any): void { this.data.set(doc._id, doc); // eslint-disable-next-line @typescript-eslint/no-var-requires this.settings.set(doc); this.insertCalls++; } - upsert(query: any, update: any): void { + updateOne(query: any, update: any): void { const existent = this.findOne(query); const data = { ...existent, ...query, ...update, ...update.$set }; @@ -71,4 +71,4 @@ class SettingsClass { export const Settings = new SettingsClass(); -mock('../../../models/server/models/Settings', Settings); +mock('@rocket.chat/models', { Settings }); diff --git a/apps/meteor/app/settings/server/index.ts b/apps/meteor/app/settings/server/index.ts index 765516219ea..05be8d6aed4 100644 --- a/apps/meteor/app/settings/server/index.ts +++ b/apps/meteor/app/settings/server/index.ts @@ -1,13 +1,23 @@ -import SettingsModel from '../../models/server/models/Settings'; +import { Settings } from '@rocket.chat/models'; + import { SettingsRegistry } from './SettingsRegistry'; import { initializeSettings } from './startup'; import { settings } from './cached'; import './applyMiddlewares'; +import { use } from './Middleware'; export { SettingsEvents } from './SettingsRegistry'; export { settings }; -export const settingsRegistry = new SettingsRegistry({ store: settings, model: SettingsModel }); +export const settingsRegistry = new SettingsRegistry({ store: settings, model: Settings }); + +settingsRegistry.add = use(settingsRegistry.add, (context, next) => { + return Promise.await(next(...context)) as any; +}); + +settingsRegistry.addGroup = use(settingsRegistry.addGroup, (context, next) => { + return Promise.await(next(...context)) as any; +}); -initializeSettings({ SettingsModel, settings }); +Promise.await(initializeSettings({ model: Settings, settings })); diff --git a/apps/meteor/app/settings/server/raw.js b/apps/meteor/app/settings/server/raw.js deleted file mode 100644 index d458f7a5561..00000000000 --- a/apps/meteor/app/settings/server/raw.js +++ /dev/null @@ -1,31 +0,0 @@ -import Settings from '../../models/server/models/Settings'; - -const cache = new Map(); - -export const setValue = (_id, value) => cache.set(_id, value); - -const setFromDB = async (_id) => { - const setting = Settings.findOneById(_id, { fields: { value: 1 } }); - if (!setting) { - return; - } - - setValue(_id, setting.value); - - return setting.value; -}; - -export const getValue = async (_id) => { - if (!cache.has(_id)) { - return setFromDB(_id); - } - - return cache.get(_id); -}; - -export const updateValue = (id, fields) => { - if (typeof fields.value === 'undefined') { - return; - } - setValue(id, fields.value); -}; diff --git a/apps/meteor/app/settings/server/raw.ts b/apps/meteor/app/settings/server/raw.ts new file mode 100644 index 00000000000..a32e3b77030 --- /dev/null +++ b/apps/meteor/app/settings/server/raw.ts @@ -0,0 +1,32 @@ +import { SettingValue } from '@rocket.chat/core-typings'; +import { Settings } from '@rocket.chat/models'; + +const cache = new Map(); + +export const setValue = (_id: string, value: SettingValue) => cache.set(_id, value); + +const setFromDB = async (_id: string) => { + const setting = await Settings.findOneById(_id, { projection: { value: 1 } }); + if (!setting) { + return; + } + + setValue(_id, setting.value); + + return setting.value; +}; + +export const getValue = async (_id: string) => { + if (!cache.has(_id)) { + return setFromDB(_id); + } + + return cache.get(_id); +}; + +export const updateValue = (id: string, fields: T) => { + if (typeof fields.value === 'undefined') { + return; + } + setValue(id, fields.value); +}; diff --git a/apps/meteor/app/settings/server/startup.ts b/apps/meteor/app/settings/server/startup.ts index 9c43be76437..2fcc1e75450 100644 --- a/apps/meteor/app/settings/server/startup.ts +++ b/apps/meteor/app/settings/server/startup.ts @@ -1,13 +1,13 @@ import type { ISetting } from '@rocket.chat/core-typings'; +import type { Settings } from '@rocket.chat/models'; -import { Settings } from '../../models/server/models/Settings'; import { ICachedSettings } from './CachedSettings'; // eslint-disable-next-line @typescript-eslint/naming-convention -export function initializeSettings({ SettingsModel, settings }: { SettingsModel: Settings; settings: ICachedSettings }): void { - SettingsModel.find().forEach((record: ISetting) => { +export async function initializeSettings({ model, settings }: { model: typeof Settings; settings: ICachedSettings }): Promise { + await model.find().forEach((record: ISetting) => { settings.set(record); }); - settings.initilized(); + settings.initialized(); } diff --git a/apps/meteor/app/statistics/server/functions/updateStatsCounter.ts b/apps/meteor/app/statistics/server/functions/updateStatsCounter.ts index 4641b040f81..f4661277202 100644 --- a/apps/meteor/app/statistics/server/functions/updateStatsCounter.ts +++ b/apps/meteor/app/statistics/server/functions/updateStatsCounter.ts @@ -1,4 +1,5 @@ -import { Settings } from '../../../models/server'; +import { Settings } from '@rocket.chat/models'; + import telemetryEvent from '../lib/telemetryEvents'; type updateCounterDataType = { settingsId: string }; diff --git a/apps/meteor/app/statistics/server/lib/statistics.ts b/apps/meteor/app/statistics/server/lib/statistics.ts index 5f1bf083f87..8d0918e9044 100644 --- a/apps/meteor/app/statistics/server/lib/statistics.ts +++ b/apps/meteor/app/statistics/server/lib/statistics.ts @@ -21,9 +21,10 @@ import { Messages as MessagesRaw, Roles as RolesRaw, InstanceStatus, + Settings, } from '@rocket.chat/models'; -import { Settings, Users, Rooms, Subscriptions, Messages } from '../../../models/server'; +import { Users, Rooms, Subscriptions, Messages } from '../../../models/server'; import { settings } from '../../../settings/server'; import { Info, getMongoInfo } from '../../../utils/server'; import { getControl } from '../../../../server/lib/migrations'; @@ -67,18 +68,21 @@ export const statistics = { // Setup Wizard statistics.wizard = {}; - wizardFields.forEach((field) => { - const record = Settings.findOne(field); - if (record) { - const wizardField = field.replace(/_/g, '').replace(field[0], field[0].toLowerCase()); - statistics.wizard[wizardField] = record.value; - } - }); + await Promise.all( + wizardFields.map(async (field) => { + const record = await Settings.findOne(field); + if (record) { + const wizardField = field.replace(/_/g, '').replace(field[0], field[0].toLowerCase()); + statistics.wizard[wizardField] = record.value; + } + }), + ); // Version + const uniqueID = await Settings.findOne('uniqueID'); statistics.uniqueId = settings.get('uniqueID'); - if (Settings.findOne('uniqueID')) { - statistics.installedAt = Settings.findOne('uniqueID').createdAt; + if (uniqueID) { + statistics.installedAt = String(uniqueID.createdAt); } if (Info) { @@ -453,6 +457,8 @@ export const statistics = { statsPms.push(Analytics.resetSeatRequestCount()); + // TODO: Is that the best way to do this? maybe we should use a promise.all() + statistics.dashboardCount = settings.get('Engagement_Dashboard_Load_Count'); statistics.messageAuditApply = settings.get('Message_Auditing_Apply_Count'); statistics.messageAuditLoad = settings.get('Message_Auditing_Panel_Load_Count'); @@ -483,22 +489,22 @@ export const statistics = { statistics.matrixBridgeEnabled = settings.get('Federation_Matrix_enabled'); statistics.uncaughtExceptionsCount = settings.get('Uncaught_Exceptions_Count'); - const defaultHomeTitle = Settings.findOneById('Layout_Home_Title').packageValue; + const defaultHomeTitle = (await Settings.findOneById('Layout_Home_Title'))?.packageValue; statistics.homeTitleChanged = settings.get('Layout_Home_Title') !== defaultHomeTitle; - const defaultHomeBody = Settings.findOneById('Layout_Home_Body').packageValue; + const defaultHomeBody = (await Settings.findOneById('Layout_Home_Body'))?.packageValue; statistics.homeBodyChanged = settings.get('Layout_Home_Body') !== defaultHomeBody; - const defaultCustomCSS = Settings.findOneById('theme-custom-css').packageValue; + const defaultCustomCSS = (await Settings.findOneById('theme-custom-css'))?.packageValue; statistics.customCSSChanged = settings.get('theme-custom-css') !== defaultCustomCSS; - const defaultOnLogoutCustomScript = Settings.findOneById('Custom_Script_On_Logout').packageValue; + const defaultOnLogoutCustomScript = (await Settings.findOneById('Custom_Script_On_Logout'))?.packageValue; statistics.onLogoutCustomScriptChanged = settings.get('Custom_Script_On_Logout') !== defaultOnLogoutCustomScript; - const defaultLoggedOutCustomScript = Settings.findOneById('Custom_Script_Logged_Out').packageValue; + const defaultLoggedOutCustomScript = (await Settings.findOneById('Custom_Script_Logged_Out'))?.packageValue; statistics.loggedOutCustomScriptChanged = settings.get('Custom_Script_Logged_Out') !== defaultLoggedOutCustomScript; - const defaultLoggedInCustomScript = Settings.findOneById('Custom_Script_Logged_In').packageValue; + const defaultLoggedInCustomScript = (await Settings.findOneById('Custom_Script_Logged_In'))?.packageValue; statistics.loggedInCustomScriptChanged = settings.get('Custom_Script_Logged_In') !== defaultLoggedInCustomScript; await Promise.all(statsPms).catch(log); diff --git a/apps/meteor/app/theme/server/server.js b/apps/meteor/app/theme/server/server.js index 07990e9c75b..1897fe731d2 100644 --- a/apps/meteor/app/theme/server/server.js +++ b/apps/meteor/app/theme/server/server.js @@ -5,11 +5,11 @@ import less from 'less'; import Autoprefixer from 'less-plugin-autoprefixer'; import { WebApp } from 'meteor/webapp'; import { Meteor } from 'meteor/meteor'; +import { Settings } from '@rocket.chat/models'; import { settings, settingsRegistry } from '../../settings/server'; import { Logger } from '../../logger'; import { addStyle } from '../../ui-master/server/inject'; -import { Settings } from '../../models/server'; const logger = new Logger('rocketchat:theme'); diff --git a/apps/meteor/app/ui-master/server/index.js b/apps/meteor/app/ui-master/server/index.js index cf837de9379..4905b5d7663 100644 --- a/apps/meteor/app/ui-master/server/index.js +++ b/apps/meteor/app/ui-master/server/index.js @@ -2,11 +2,12 @@ import { Meteor } from 'meteor/meteor'; import { Inject } from 'meteor/meteorhacks:inject-initial'; import { Tracker } from 'meteor/tracker'; import _ from 'underscore'; +import { Settings } from '@rocket.chat/models'; import { escapeHTML } from '@rocket.chat/string-helpers'; -import { Settings } from '../../models/server'; import { settings } from '../../settings/server'; import { applyHeadInjections, headInjections, injectIntoBody, injectIntoHead } from './inject'; + import './scripts'; export * from './inject'; @@ -124,16 +125,11 @@ Meteor.startup(() => { }); const renderDynamicCssList = _.debounce( - Meteor.bindEnvironment(() => { + Meteor.bindEnvironment(async () => { // const variables = RocketChat.models.Settings.findOne({_id:'theme-custom-variables'}, {fields: { value: 1}}); - const colors = Settings.find({ _id: /theme-color-rc/i }, { fields: { value: 1, editor: 1 } }) - .fetch() - .filter((color) => color && color.value); - - if (!colors) { - return; - } + const colors = await Settings.find({ _id: /theme-color-rc/i }, { projection: { value: 1, editor: 1 } }).toArray(); const css = colors + .filter((color) => color && color.value) .map(({ _id, value, editor }) => { if (editor === 'expression') { return `--${_id.replace('theme-color-', '')}: var(--${value});`; diff --git a/apps/meteor/app/version-check/server/functions/checkVersionUpdate.js b/apps/meteor/app/version-check/server/functions/checkVersionUpdate.js deleted file mode 100644 index 8a3191a9863..00000000000 --- a/apps/meteor/app/version-check/server/functions/checkVersionUpdate.js +++ /dev/null @@ -1,93 +0,0 @@ -import { TAPi18n } from 'meteor/rocketchat:tap-i18n'; -import semver from 'semver'; - -import getNewUpdates from './getNewUpdates'; -import { settings } from '../../../settings/server'; -import { Info } from '../../../utils'; -import { Users, Settings } from '../../../models/server'; -import logger from '../logger'; -import { sendMessagesToAdmins } from '../../../../server/lib/sendMessagesToAdmins'; -// import getNewUpdates from '../sampleUpdateData'; - -export default () => { - logger.info('Checking for version updates'); - - const { versions, alerts } = getNewUpdates(); - - const update = { - exists: false, - lastestVersion: null, - security: false, - }; - - const lastCheckedVersion = settings.get('Update_LatestAvailableVersion'); - versions.forEach((version) => { - if (semver.lte(version.version, lastCheckedVersion)) { - return; - } - - if (semver.lte(version.version, Info.version)) { - return; - } - - update.exists = true; - update.lastestVersion = version; - - if (version.security === true) { - update.security = true; - } - }); - - if (update.exists) { - Settings.updateValueById('Update_LatestAvailableVersion', update.lastestVersion.version); - - Promise.await( - sendMessagesToAdmins({ - msgs: ({ adminUser }) => [ - { - msg: `*${TAPi18n.__('Update_your_RocketChat', adminUser.language)}*\n${TAPi18n.__( - 'New_version_available_(s)', - update.lastestVersion.version, - adminUser.language, - )}\n${update.lastestVersion.infoUrl}`, - }, - ], - banners: [ - { - id: `versionUpdate-${update.lastestVersion.version}`.replace(/\./g, '_'), - priority: 10, - title: 'Update_your_RocketChat', - text: 'New_version_available_(s)', - textArguments: [update.lastestVersion.version], - link: update.lastestVersion.infoUrl, - }, - ], - }), - ); - } - - if (alerts && alerts.length) { - Promise.await( - sendMessagesToAdmins({ - msgs: ({ adminUser }) => - alerts - .filter((alert) => !Users.bannerExistsById(adminUser._id, `alert-${alert.id}`)) - .map((alert) => ({ - msg: `*${TAPi18n.__('Rocket_Chat_Alert', adminUser.language)}:*\n\n*${TAPi18n.__( - alert.title, - adminUser.language, - )}*\n${TAPi18n.__(alert.text, ...(alert.textArguments || []), adminUser.language)}\n${alert.infoUrl}`, - })), - banners: alerts.map((alert) => ({ - id: `alert-${alert.id}`.replace(/\./g, '_'), - priority: 10, - title: alert.title, - text: alert.text, - textArguments: alert.textArguments, - modifiers: alert.modifiers, - link: alert.infoUrl, - })), - }), - ); - } -}; diff --git a/apps/meteor/app/version-check/server/functions/checkVersionUpdate.ts b/apps/meteor/app/version-check/server/functions/checkVersionUpdate.ts new file mode 100644 index 00000000000..6577cd41292 --- /dev/null +++ b/apps/meteor/app/version-check/server/functions/checkVersionUpdate.ts @@ -0,0 +1,90 @@ +import { TAPi18n } from 'meteor/rocketchat:tap-i18n'; +import semver from 'semver'; +import { Settings } from '@rocket.chat/models'; + +import { getNewUpdates } from './getNewUpdates'; +import { settings } from '../../../settings/server'; +import { Info } from '../../../utils/server'; +import { Users } from '../../../models/server'; +import logger from '../logger'; +import { sendMessagesToAdmins } from '../../../../server/lib/sendMessagesToAdmins'; +// import getNewUpdates from '../sampleUpdateData'; + +export const checkVersionUpdate = async () => { + logger.info('Checking for version updates'); + + const { versions, alerts } = await getNewUpdates(); + + const lastCheckedVersion = settings.get('Update_LatestAvailableVersion'); + + for await (const version of versions) { + if (!lastCheckedVersion) { + break; + } + if (semver.lte(version.version, lastCheckedVersion)) { + continue; + } + + if (semver.lte(version.version, Info.version)) { + continue; + } + + await Settings.updateValueById('Update_LatestAvailableVersion', version.version); + + await sendMessagesToAdmins({ + msgs: ({ adminUser }) => [ + { + msg: `*${TAPi18n.__('Update_your_RocketChat', { ...(adminUser.language && { lng: adminUser.language }) })}*\n${TAPi18n.__( + 'New_version_available_(s)', + { + postProcess: 'sprintf', + sprintf: [version.version], + }, + )}\n${version.infoUrl}`, + }, + ], + banners: [ + { + id: `versionUpdate-${version.version}`.replace(/\./g, '_'), + priority: 10, + title: 'Update_your_RocketChat', + text: 'New_version_available_(s)', + textArguments: [version.version], + link: version.infoUrl, + modifiers: [], + }, + ], + }); + break; + } + + if (alerts && alerts.length) { + await sendMessagesToAdmins({ + msgs: ({ adminUser }) => + alerts + .filter((alert) => !Users.bannerExistsById(adminUser._id, `alert-${alert.id}`)) + .map((alert) => ({ + msg: `*${TAPi18n.__('Rocket_Chat_Alert', { ...(adminUser.language && { lng: adminUser.language }) })}:*\n\n*${TAPi18n.__( + alert.title, + { ...(adminUser.language && { lng: adminUser.language }) }, + )}*\n${TAPi18n.__(alert.text, { + ...(adminUser.language && { lng: adminUser.language }), + ...(Array.isArray(alert.textArguments) && { + postProcess: 'sprintf', + sprintf: alert.textArguments, + }), + ...((!Array.isArray(alert.textArguments) && alert.textArguments) || {}), // bien dormido + })}\n${alert.infoUrl}`, + })), + banners: alerts.map((alert) => ({ + id: `alert-${alert.id}`.replace(/\./g, '_'), + priority: 10, + title: alert.title, + text: alert.text, + textArguments: alert.textArguments, + modifiers: alert.modifiers, + link: alert.infoUrl, + })), + }); + } +}; diff --git a/apps/meteor/app/version-check/server/functions/getNewUpdates.js b/apps/meteor/app/version-check/server/functions/getNewUpdates.js deleted file mode 100644 index f5e8e7c0c69..00000000000 --- a/apps/meteor/app/version-check/server/functions/getNewUpdates.js +++ /dev/null @@ -1,72 +0,0 @@ -import os from 'os'; - -import { HTTP } from 'meteor/http'; -import { check, Match } from 'meteor/check'; - -import { Settings } from '../../../models/server'; -import { Info } from '../../../utils'; -import { getWorkspaceAccessToken } from '../../../cloud/server'; - -export default () => { - try { - const uniqueID = Settings.findOne('uniqueID'); - - const params = { - uniqueId: uniqueID.value, - installedAt: uniqueID.createdAt.toJSON(), - version: Info.version, - osType: os.type(), - osPlatform: os.platform(), - osArch: os.arch(), - osRelease: os.release(), - nodeVersion: process.version, - deployMethod: process.env.DEPLOY_METHOD || 'tar', - deployPlatform: process.env.DEPLOY_PLATFORM || 'selfinstall', - }; - - const headers = {}; - const token = getWorkspaceAccessToken(); - if (token) { - headers.Authorization = `Bearer ${token}`; - } - - const { data } = HTTP.get('https://releases.rocket.chat/updates/check', { - params, - headers, - }); - - check( - data, - Match.ObjectIncluding({ - versions: [ - Match.ObjectIncluding({ - version: Match.Optional(String), - security: Match.Optional(Boolean), - infoUrl: Match.Optional(String), - }), - ], - alerts: Match.Optional([ - Match.ObjectIncluding({ - id: Match.Optional(String), - title: Match.Optional(String), - text: Match.Optional(String), - textArguments: Match.Optional([Match.Any]), - modifiers: Match.Optional([String]), - infoUrl: Match.Optional(String), - }), - ]), - }), - ); - - return data; - } catch (error) { - // There's no need to log this error - // as it's pointless and the user - // can't do anything about it anyways - - return { - versions: [], - alerts: [], - }; - } -}; diff --git a/apps/meteor/app/version-check/server/functions/getNewUpdates.ts b/apps/meteor/app/version-check/server/functions/getNewUpdates.ts new file mode 100644 index 00000000000..44bf58c3e7c --- /dev/null +++ b/apps/meteor/app/version-check/server/functions/getNewUpdates.ts @@ -0,0 +1,93 @@ +import os from 'os'; + +import { Settings } from '@rocket.chat/models'; +import { HTTP } from 'meteor/http'; +import { check, Match } from 'meteor/check'; + +import { Info } from '../../../utils/server'; +import { getWorkspaceAccessToken } from '../../../cloud/server'; + +export const getNewUpdates = async () => { + try { + const uniqueID = await Settings.findOne('uniqueID'); + + if (!uniqueID) { + throw new Error('uniqueID not found'); + } + + const params = { + uniqueId: String(uniqueID.value), + installedAt: uniqueID.createdAt.toJSON(), + version: Info.version, + osType: os.type(), + osPlatform: os.platform(), + osArch: os.arch(), + osRelease: os.release(), + nodeVersion: process.version, + deployMethod: process.env.DEPLOY_METHOD || 'tar', + deployPlatform: process.env.DEPLOY_PLATFORM || 'selfinstall', + }; + + const token = await getWorkspaceAccessToken(); + const headers = { + ...(token && { Authorization: `Bearer ${token}` }), + }; + + const { data } = HTTP.get('https://releases.rocket.chat/updates/check', { + params, + headers, + }); + + check( + data, + Match.ObjectIncluding({ + versions: [ + Match.ObjectIncluding({ + version: String, + security: Match.Optional(Boolean), + infoUrl: String, + }), + ], + alerts: [ + Match.Optional([ + Match.ObjectIncluding({ + id: String, + title: String, + text: String, + textArguments: [Match.Any], + modifiers: [String], + infoUrl: String, + }), + ]), + ], + }), + ); + + return data as { + versions: { + version: string; + security: boolean; + infoUrl: string; + }[]; + + alerts: { + id: string; + priority: number; + title: string; + text: string; + textArguments?: string[]; + modifiers: string[]; + infoUrl: string; + }[]; + }; + } catch (error) { + // There's no need to log this error + // as it's pointless and the user + // can't do anything about it anyways + + return { + versions: [], + alerts: [], + }; + } +}; diff --git a/apps/meteor/app/version-check/server/index.ts b/apps/meteor/app/version-check/server/index.ts index 9400ea1c6b9..4f351385877 100644 --- a/apps/meteor/app/version-check/server/index.ts +++ b/apps/meteor/app/version-check/server/index.ts @@ -2,7 +2,7 @@ import { Meteor } from 'meteor/meteor'; import { SyncedCron } from 'meteor/littledata:synced-cron'; import { settings } from '../../settings/server'; -import checkVersionUpdate from './functions/checkVersionUpdate'; +import { checkVersionUpdate } from './functions/checkVersionUpdate'; import './methods/banner_dismiss'; import './addSettings'; @@ -17,7 +17,7 @@ const addVersionCheckJob = Meteor.bindEnvironment(() => { name: jobName, schedule: (parser: { text: (time: string) => string }) => parser.text('at 2:00 am'), job() { - checkVersionUpdate(); + Promise.await(checkVersionUpdate()); }, }); }); @@ -25,7 +25,7 @@ const addVersionCheckJob = Meteor.bindEnvironment(() => { Meteor.startup(() => { Meteor.defer(() => { if (settings.get('Register_Server') && settings.get('Update_EnableChecker')) { - checkVersionUpdate(); + Promise.await(checkVersionUpdate()); } }); }); diff --git a/apps/meteor/definition/externals/meteor/rocketchat-tap-i18n.d.ts b/apps/meteor/definition/externals/meteor/rocketchat-tap-i18n.d.ts index 91fd9097c4c..bb1c393a29b 100644 --- a/apps/meteor/definition/externals/meteor/rocketchat-tap-i18n.d.ts +++ b/apps/meteor/definition/externals/meteor/rocketchat-tap-i18n.d.ts @@ -9,7 +9,14 @@ declare module 'meteor/rocketchat:tap-i18n' { lng?: string; } & { [replacements: string]: boolean | number | string | string[]; - }, + } & ( + | { + postProcess: 'sprintf'; + sprintf: (boolean | number | string)[]; + } + | {} + ), + lang?: string, ): string; function getLanguages(): { [language: string]: { diff --git a/apps/meteor/ee/app/canned-responses/server/index.js b/apps/meteor/ee/app/canned-responses/server/index.js index b85fa231ada..fdcd77ec832 100644 --- a/apps/meteor/ee/app/canned-responses/server/index.js +++ b/apps/meteor/ee/app/canned-responses/server/index.js @@ -1,5 +1,3 @@ -import { Meteor } from 'meteor/meteor'; - import { onLicense } from '../../license/server'; onLicense('canned-responses', () => { @@ -11,7 +9,5 @@ onLicense('canned-responses', () => { require('./methods/saveCannedResponse'); require('./methods/removeCannedResponse'); - Meteor.startup(function () { - createSettings(); - }); + createSettings(); }); diff --git a/apps/meteor/ee/app/license/server/getSeatsRequestLink.ts b/apps/meteor/ee/app/license/server/getSeatsRequestLink.ts index 0237eb094d2..b911f011372 100644 --- a/apps/meteor/ee/app/license/server/getSeatsRequestLink.ts +++ b/apps/meteor/ee/app/license/server/getSeatsRequestLink.ts @@ -1,15 +1,16 @@ import type { ISetting } from '@rocket.chat/core-typings'; +import { Settings } from '@rocket.chat/models'; -import { Settings, Users } from '../../../../app/models/server'; +import { Users } from '../../../../app/models/server'; type WizardSettings = Array; const url = 'https://go.rocket.chat/i/seats-cap-upgrade'; -export const getSeatsRequestLink = (): string => { - const workspaceId: ISetting | undefined = Settings.findOneById('Cloud_Workspace_Id'); +export const getSeatsRequestLink = async (): Promise => { + const workspaceId = await Settings.findOneById('Cloud_Workspace_Id'); const activeUsers = Users.getActiveLocalUserCount(); - const wizardSettings: WizardSettings = Settings.findSetupWizardSettings().fetch(); + const wizardSettings: WizardSettings = await Settings.findSetupWizardSettings().toArray(); const newUrl = new URL(url); diff --git a/apps/meteor/ee/app/license/server/settings.js b/apps/meteor/ee/app/license/server/settings.js index f966c0bf5ee..27867ee7fcc 100644 --- a/apps/meteor/ee/app/license/server/settings.js +++ b/apps/meteor/ee/app/license/server/settings.js @@ -1,7 +1,7 @@ +import { Settings } from '@rocket.chat/models'; import { Meteor } from 'meteor/meteor'; import { settings, settingsRegistry } from '../../../../app/settings/server'; -import { Settings } from '../../../../app/models/server'; import { addLicense } from './license'; Meteor.startup(function () { diff --git a/apps/meteor/ee/app/livechat-enterprise/server/settings.ts b/apps/meteor/ee/app/livechat-enterprise/server/settings.ts index 1a68a16a6e6..a217874a553 100644 --- a/apps/meteor/ee/app/livechat-enterprise/server/settings.ts +++ b/apps/meteor/ee/app/livechat-enterprise/server/settings.ts @@ -1,10 +1,11 @@ +import { Settings } from '@rocket.chat/models'; + import { settingsRegistry } from '../../../../app/settings/server'; -import { Settings } from '../../../../app/models/server'; const omnichannelEnabledQuery = { _id: 'Livechat_enabled', value: true }; const businessHoursEnabled = { _id: 'Livechat_enable_business_hours', value: true }; -export const createSettings = (): void => { +export const createSettings = async (): Promise => { settingsRegistry.add('Livechat_abandoned_rooms_action', 'none', { type: 'select', group: 'Omnichannel', @@ -206,11 +207,11 @@ export const createSettings = (): void => { enableQuery: omnichannelEnabledQuery, }); - Settings.addOptionValueById('Livechat_Routing_Method', { + await Settings.addOptionValueById('Livechat_Routing_Method', { key: 'Load_Balancing', i18nLabel: 'Load_Balancing', }); - Settings.addOptionValueById('Livechat_Routing_Method', { + await Settings.addOptionValueById('Livechat_Routing_Method', { key: 'Load_Rotation', i18nLabel: 'Load_Rotation', }); diff --git a/apps/meteor/ee/app/settings/server/settings.ts b/apps/meteor/ee/app/settings/server/settings.ts index a4da8d0ed4e..7f0558e2c8d 100644 --- a/apps/meteor/ee/app/settings/server/settings.ts +++ b/apps/meteor/ee/app/settings/server/settings.ts @@ -1,8 +1,8 @@ import { Meteor } from 'meteor/meteor'; import { ISetting, SettingValue } from '@rocket.chat/core-typings'; +import { Settings } from '@rocket.chat/models'; import { isEnterprise, hasLicense, onValidateLicenses } from '../../license/server/license'; -import SettingsModel from '../../../../app/models/server/models/Settings'; import { use } from '../../../../app/settings/server/Middleware'; import { settings, SettingsEvents } from '../../../../app/settings/server'; @@ -50,7 +50,7 @@ SettingsEvents.on('fetch-settings', (settings: Array): void => { }); function updateSettings(): void { - const enterpriseSettings = SettingsModel.findEnterpriseSettings(); + const enterpriseSettings = Promise.await(Settings.findEnterpriseSettings()); enterpriseSettings.forEach((record: ISetting) => settings.set(record)); } diff --git a/apps/meteor/ee/server/api/licenses.ts b/apps/meteor/ee/server/api/licenses.ts index f7a5d75dbfb..5e3f8eb25d7 100644 --- a/apps/meteor/ee/server/api/licenses.ts +++ b/apps/meteor/ee/server/api/licenses.ts @@ -1,7 +1,8 @@ import { check } from 'meteor/check'; +import { Settings } from '@rocket.chat/models'; import { getLicenses, validateFormat, flatModules, getMaxActiveUsers, isEnterprise } from '../../app/license/server/license'; -import { Settings, Users } from '../../../app/models/server'; +import { Users } from '../../../app/models/server'; import { API } from '../../../app/api/server/api'; import { hasPermission } from '../../../app/authorization/server'; import { ILicense } from '../../app/license/definitions/ILicense'; @@ -35,7 +36,7 @@ API.v1.addRoute( 'licenses.add', { authRequired: true }, { - post() { + async post() { check(this.bodyParams, { license: String, }); @@ -49,7 +50,7 @@ API.v1.addRoute( return API.v1.failure('Invalid license'); } - Settings.updateValueById('Enterprise_License', license); + await Settings.updateValueById('Enterprise_License', license); return API.v1.success(); }, diff --git a/apps/meteor/ee/server/requestSeatsRoute.ts b/apps/meteor/ee/server/requestSeatsRoute.ts index a7e08e5e0ad..8c9e09f21eb 100644 --- a/apps/meteor/ee/server/requestSeatsRoute.ts +++ b/apps/meteor/ee/server/requestSeatsRoute.ts @@ -10,7 +10,7 @@ Meteor.startup(() => { WebApp.connectHandlers.use( '/requestSeats/', Meteor.bindEnvironment((_: IncomingMessage, res: ServerResponse) => { - const url = getSeatsRequestLink(); + const url = Promise.await(getSeatsRequestLink()); Analytics.saveSeatRequest(); res.writeHead(302, { Location: url }); diff --git a/apps/meteor/ee/server/startup/upsell.ts b/apps/meteor/ee/server/startup/upsell.ts index f979febe391..8f9dfaf1ea2 100644 --- a/apps/meteor/ee/server/startup/upsell.ts +++ b/apps/meteor/ee/server/startup/upsell.ts @@ -1,6 +1,6 @@ import { Meteor } from 'meteor/meteor'; +import { Settings } from '@rocket.chat/models'; -import { Settings } from '../../../app/models/server'; import { onValidateLicenses, getLicenses } from '../../app/license/server/license'; const handleHadTrial = (): void => { diff --git a/apps/meteor/server/cron/federation.ts b/apps/meteor/server/cron/federation.ts index f35bec41756..bedebb434fe 100644 --- a/apps/meteor/server/cron/federation.ts +++ b/apps/meteor/server/cron/federation.ts @@ -17,7 +17,7 @@ function updateSetting(id: string, value: SettingValue | null): void { Settings.updateValueById(id, value); } } else { - Settings.updateValueById(id, undefined); + Settings.updateValueById(id, null); } } diff --git a/apps/meteor/server/cron/statistics.js b/apps/meteor/server/cron/statistics.js index 1c8a382cd36..2910a158bb2 100644 --- a/apps/meteor/server/cron/statistics.js +++ b/apps/meteor/server/cron/statistics.js @@ -16,7 +16,7 @@ async function generateStatistics(logger) { try { const headers = {}; - const token = getWorkspaceAccessToken(); + const token = await getWorkspaceAccessToken(); if (token) { headers.Authorization = `Bearer ${token}`; diff --git a/apps/meteor/server/lib/sendMessagesToAdmins.ts b/apps/meteor/server/lib/sendMessagesToAdmins.ts index 321e9c6a44e..c26adaa8888 100644 --- a/apps/meteor/server/lib/sendMessagesToAdmins.ts +++ b/apps/meteor/server/lib/sendMessagesToAdmins.ts @@ -33,7 +33,7 @@ export async function sendMessagesToAdmins({ }: { fromId?: string; checkFrom?: boolean; - msgs?: Partial[] | Function; + msgs?: Partial[] | (({ adminUser }: { adminUser: IUser }) => Partial[]); banners?: Banner[] | Function; }): Promise { const fromUser = checkFrom ? await Users.findOneById(fromId, { projection: { _id: 1 } }) : true; diff --git a/apps/meteor/server/methods/getSetupWizardParameters.js b/apps/meteor/server/methods/getSetupWizardParameters.ts similarity index 66% rename from apps/meteor/server/methods/getSetupWizardParameters.js rename to apps/meteor/server/methods/getSetupWizardParameters.ts index a26ab8f329b..56fae089374 100644 --- a/apps/meteor/server/methods/getSetupWizardParameters.js +++ b/apps/meteor/server/methods/getSetupWizardParameters.ts @@ -1,11 +1,11 @@ +import { Settings } from '@rocket.chat/models'; import { Meteor } from 'meteor/meteor'; -import { Settings } from '../../app/models/server'; import { settings } from '../../app/settings/server'; Meteor.methods({ - getSetupWizardParameters() { - const setupWizardSettings = Settings.findSetupWizardSettings().fetch(); + async getSetupWizardParameters() { + const setupWizardSettings = await Settings.findSetupWizardSettings().toArray(); const serverAlreadyRegistered = !!settings.get('Cloud_Workspace_Client_Id') || process.env.DEPLOY_PLATFORM === 'rocket-cloud'; return { diff --git a/apps/meteor/server/models/raw/Settings.ts b/apps/meteor/server/models/raw/Settings.ts index eaccab8e772..3a5d150c015 100644 --- a/apps/meteor/server/models/raw/Settings.ts +++ b/apps/meteor/server/models/raw/Settings.ts @@ -50,7 +50,10 @@ export class SettingsRaw extends BaseRaw implements ISettingsModel { return this.find(query); } - updateValueById(_id: string, value: T): Promise { + updateValueById( + _id: string, + value: (ISetting['value'] extends undefined ? never : ISetting['value']) | null, + ): Promise { const query = { blocked: { $ne: true }, value: { $ne: value }, @@ -207,4 +210,8 @@ export class SettingsRaw extends BaseRaw implements ISettingsModel { }, }); } + + findEnterpriseSettings(): FindCursor { + return this.find({ enterprise: true }); + } } diff --git a/apps/meteor/server/services/nps/getAndCreateNpsSurvey.ts b/apps/meteor/server/services/nps/getAndCreateNpsSurvey.ts index a4de08f90e2..5c7efb8d802 100644 --- a/apps/meteor/server/services/nps/getAndCreateNpsSurvey.ts +++ b/apps/meteor/server/services/nps/getAndCreateNpsSurvey.ts @@ -18,7 +18,7 @@ type NpsSurveyData = { }; export const getAndCreateNpsSurvey = Meteor.bindEnvironment(async function getNpsSurvey(npsId: string) { - const token = getWorkspaceAccessToken(); + const token = await getWorkspaceAccessToken(); if (!token) { return false; } diff --git a/apps/meteor/server/services/nps/sendNpsResults.ts b/apps/meteor/server/services/nps/sendNpsResults.ts index 62751c691a0..b7c585bf7de 100644 --- a/apps/meteor/server/services/nps/sendNpsResults.ts +++ b/apps/meteor/server/services/nps/sendNpsResults.ts @@ -12,7 +12,7 @@ type NPSResultPayload = { }; export const sendNpsResults = Meteor.bindEnvironment(function sendNpsResults(npsId: string, data: NPSResultPayload) { - const token = getWorkspaceAccessToken(); + const token = Promise.await(getWorkspaceAccessToken()); if (!token) { return false; } diff --git a/apps/meteor/server/startup/migrations/v246.ts b/apps/meteor/server/startup/migrations/v246.ts index 826b5c12579..f7a2d05994c 100644 --- a/apps/meteor/server/startup/migrations/v246.ts +++ b/apps/meteor/server/startup/migrations/v246.ts @@ -1,5 +1,6 @@ +import { Settings } from '@rocket.chat/models'; + import { addMigration } from '../../lib/migrations'; -import { Settings } from '../../../app/models/server'; import { settings } from '../../../app/settings/server'; addMigration({ @@ -7,11 +8,12 @@ addMigration({ up() { const livechatVideoCallEnabled = settings.get('Livechat_videocall_enabled'); if (livechatVideoCallEnabled) { - Settings.upsert( + Settings.updateOne( { _id: 'Omnichannel_call_provider' }, { $set: { value: 'Jitsi' }, }, + { upsert: true }, ); } Settings.removeById('Livechat_videocall_enabled'); @@ -20,11 +22,12 @@ addMigration({ const webRTCEnableDirect = settings.get('WebRTC_Enable_Direct'); const webRTCEnablePrivate = settings.get('WebRTC_Enable_Private'); if (webRTCEnableChannel || webRTCEnableDirect || webRTCEnablePrivate) { - Settings.upsert( + Settings.updateOne( { _id: 'WebRTC_Enabled' }, { $set: { value: true }, }, + { upsert: true }, ); } }, diff --git a/apps/meteor/tests/unit/app/settings/server/functions/settings.tests.ts b/apps/meteor/tests/unit/app/settings/server/functions/settings.tests.ts index 507a3d8e392..d2f2fd04630 100644 --- a/apps/meteor/tests/unit/app/settings/server/functions/settings.tests.ts +++ b/apps/meteor/tests/unit/app/settings/server/functions/settings.tests.ts @@ -11,12 +11,12 @@ describe('Settings', () => { process.env = {}; }); - it('should not insert the same setting twice', () => { + it('should not insert the same setting twice', async () => { const settings = new CachedSettings(); Settings.settings = settings; - settings.initilized(); + settings.initialized(); const settingsRegistry = new SettingsRegistry({ store: settings, model: Settings as any }); - settingsRegistry.addGroup('group', function () { + await settingsRegistry.addGroup('group', function () { this.section('section', function () { this.add('my_setting', true, { type: 'boolean', @@ -44,7 +44,7 @@ describe('Settings', () => { autocomplete: true, }); - settingsRegistry.addGroup('group', function () { + await settingsRegistry.addGroup('group', function () { this.section('section', function () { this.add('my_setting', true, { type: 'boolean', @@ -58,7 +58,7 @@ describe('Settings', () => { expect(Settings.findOne({ _id: 'my_setting' }).value).to.be.equal(true); - settingsRegistry.addGroup('group', function () { + await settingsRegistry.addGroup('group', function () { this.section('section', function () { this.add('my_setting2', false, { type: 'boolean', @@ -74,15 +74,15 @@ describe('Settings', () => { expect(Settings.findOne({ _id: 'my_setting2' }).value).to.be.equal(false); }); - it('should respect override via environment as int', () => { + it('should respect override via environment as int', async () => { const settings = new CachedSettings(); Settings.settings = settings; - settings.initilized(); + settings.initialized(); const settingsRegistry = new SettingsRegistry({ store: settings, model: Settings as any }); process.env.OVERWRITE_SETTING_my_setting = '1'; - settingsRegistry.addGroup('group', function () { + await settingsRegistry.addGroup('group', function () { this.section('section', function () { this.add('my_setting', 0, { type: 'int', @@ -114,7 +114,7 @@ describe('Settings', () => { process.env.OVERWRITE_SETTING_my_setting = '2'; - settingsRegistry.addGroup('group', function () { + await settingsRegistry.addGroup('group', function () { this.section('section', function () { this.add('my_setting', 0, { type: 'int', @@ -132,14 +132,14 @@ describe('Settings', () => { }); }); - it('should respect override via environment as boolean', () => { + it('should respect override via environment as boolean', async () => { process.env.OVERWRITE_SETTING_my_setting_bool = 'true'; const settings = new CachedSettings(); Settings.settings = settings; - settings.initilized(); + settings.initialized(); const settingsRegistry = new SettingsRegistry({ store: settings, model: Settings as any }); - settingsRegistry.addGroup('group', function () { + await settingsRegistry.addGroup('group', function () { this.section('section', function () { this.add('my_setting_bool', false, { type: 'boolean', @@ -171,7 +171,7 @@ describe('Settings', () => { process.env.OVERWRITE_SETTING_my_setting_bool = 'false'; - settingsRegistry.addGroup('group', function () { + await settingsRegistry.addGroup('group', function () { this.section('section', function () { this.add('my_setting_bool', true, { type: 'boolean', @@ -189,14 +189,14 @@ describe('Settings', () => { }); }); - it('should respect override via environment as string', () => { + it('should respect override via environment as string', async () => { process.env.OVERWRITE_SETTING_my_setting_str = 'hey'; const settings = new CachedSettings(); Settings.settings = settings; - settings.initilized(); + settings.initialized(); const settingsRegistry = new SettingsRegistry({ store: settings, model: Settings as any }); - settingsRegistry.addGroup('group', function () { + await settingsRegistry.addGroup('group', function () { this.section('section', function () { this.add('my_setting_str', '', { type: 'string', @@ -228,7 +228,7 @@ describe('Settings', () => { process.env.OVERWRITE_SETTING_my_setting_str = 'hey ho'; - settingsRegistry.addGroup('group', function () { + await settingsRegistry.addGroup('group', function () { this.section('section', function () { this.add('my_setting_str', 'hey', { type: 'string', @@ -247,14 +247,14 @@ describe('Settings', () => { }); }); - it('should respect initial value via environment', () => { + it('should respect initial value via environment', async () => { process.env.my_setting = '1'; const settings = new CachedSettings(); Settings.settings = settings; - settings.initilized(); + settings.initialized(); const settingsRegistry = new SettingsRegistry({ store: settings, model: Settings as any }); - settingsRegistry.addGroup('group', function () { + await settingsRegistry.addGroup('group', function () { this.section('section', function () { this.add('my_setting', 0, { type: 'int', @@ -284,7 +284,7 @@ describe('Settings', () => { expect(Settings.upsertCalls).to.be.equal(0); expect(Settings.findOne({ _id: 'my_setting' })).to.include(expectedSetting); - settingsRegistry.addGroup('group', function () { + await settingsRegistry.addGroup('group', function () { this.section('section', function () { this.add('my_setting', 0, { type: 'int', @@ -298,33 +298,35 @@ describe('Settings', () => { expect(Settings.findOne({ _id: 'my_setting' })).to.include({ ...expectedSetting }); }); - it('should call `settings.get` callback on setting added', (done) => { - const settings = new CachedSettings(); - Settings.settings = settings; - settings.initilized(); - const settingsRegistry = new SettingsRegistry({ store: settings, model: Settings as any }); - - const spiedCallback1 = spy(); - const spiedCallback2 = spy(); - - settingsRegistry.addGroup('group', function () { - this.section('section', function () { - this.add('setting_callback', 'value1', { - type: 'string', + it('should call `settings.get` callback on setting added', async () => { + return new Promise(async (resolve) => { + const settings = new CachedSettings(); + Settings.settings = settings; + settings.initialized(); + const settingsRegistry = new SettingsRegistry({ store: settings, model: Settings as any }); + + const spiedCallback1 = spy(); + const spiedCallback2 = spy(); + + await settingsRegistry.addGroup('group', function () { + this.section('section', function () { + this.add('setting_callback', 'value1', { + type: 'string', + }); }); }); - }); - settings.watch('setting_callback', spiedCallback1, { debounce: 10 }); - settings.watchByRegex(/setting_callback/, spiedCallback2, { debounce: 10 }); + settings.watch('setting_callback', spiedCallback1, { debounce: 10 }); + settings.watchByRegex(/setting_callback/, spiedCallback2, { debounce: 10 }); - setTimeout(() => { - expect(spiedCallback1).to.have.been.called.exactly(1); - expect(spiedCallback2).to.have.been.called.exactly(1); - expect(spiedCallback1).to.have.been.called.always.with('value1'); - expect(spiedCallback2).to.have.been.called.always.with('setting_callback', 'value1'); - done(); - }, settings.getConfig({ debounce: 10 }).debounce); + setTimeout(() => { + expect(spiedCallback1).to.have.been.called.exactly(1); + expect(spiedCallback2).to.have.been.called.exactly(1); + expect(spiedCallback1).to.have.been.called.always.with('value1'); + expect(spiedCallback2).to.have.been.called.always.with('setting_callback', 'value1'); + resolve(); + }, settings.getConfig({ debounce: 10 }).debounce); + }); }); it('should call `settings.watch` callback on setting changed registering before initialized', (done) => { @@ -337,7 +339,7 @@ describe('Settings', () => { settings.watch('setting_callback', spiedCallback1, { debounce: 1 }); settings.watchByRegex(/setting_callback/gi, spiedCallback2, { debounce: 1 }); - settings.initilized(); + settings.initialized(); settingsRegistry.addGroup('group', function () { this.section('section', function () { this.add('setting_callback', 'value2', { diff --git a/apps/meteor/tests/unit/app/settings/server/raw.tests.js b/apps/meteor/tests/unit/app/settings/server/raw.tests.js deleted file mode 100644 index 4aa6fe69f19..00000000000 --- a/apps/meteor/tests/unit/app/settings/server/raw.tests.js +++ /dev/null @@ -1,45 +0,0 @@ -import { expect, spy } from 'chai'; -import rewire from 'rewire'; - -describe('Raw Settings', () => { - let rawModule; - const cache = new Map(); - - before('rewire deps', () => { - const spied = spy(async (id) => { - if (id === '1') { - return 'some-setting-value'; - } - return null; - }); - - rawModule = rewire('../../../../../app/settings/server/raw'); - rawModule.__set__('setFromDB', spied); - rawModule.__set__('cache', cache); - }); - - it('should get the value from database when it isnt in cache', async () => { - const setting = await rawModule.getValue('1'); - - expect(setting).to.be.equal('some-setting-value'); - }); - - it('should get the value from cache when its available', async () => { - cache.set('2', 'supeer-setting'); - const setting = await rawModule.getValue('2'); - - expect(setting).to.be.equal('supeer-setting'); - }); - - it('should update the value in cache', async () => { - await rawModule.updateValue('2', { value: 'not-super-setting' }); - - expect(cache.get('2')).to.be.equal('not-super-setting'); - }); - - it('should not update the setting if the new value is undefined', async () => { - await rawModule.updateValue('2', {}); - - expect(cache.get('2')).to.be.equal('not-super-setting'); - }); -}); diff --git a/packages/core-typings/src/ISetting.ts b/packages/core-typings/src/ISetting.ts index bd7f5d461e7..99b7e2cc27c 100644 --- a/packages/core-typings/src/ISetting.ts +++ b/packages/core-typings/src/ISetting.ts @@ -12,7 +12,7 @@ export enum SettingEditor { type AssetValue = { defaultUrl?: string }; export type SettingValueMultiSelect = (string | number)[]; export type SettingValueRoomPick = Array<{ _id: string; name: string }> | string; -export type SettingValue = string | boolean | number | SettingValueMultiSelect | Date | AssetValue | undefined; +export type SettingValue = string | boolean | number | SettingValueMultiSelect | Date | AssetValue | undefined | null; export interface ISettingSelectOption { key: string | number; diff --git a/packages/model-typings/src/models/ISettingsModel.ts b/packages/model-typings/src/models/ISettingsModel.ts index 03079670fe1..ea7ab2d813f 100644 --- a/packages/model-typings/src/models/ISettingsModel.ts +++ b/packages/model-typings/src/models/ISettingsModel.ts @@ -12,7 +12,10 @@ export interface ISettingsModel extends IBaseModel { findByIds(_id?: string[] | string): FindCursor; - updateValueById(_id: string, value: T): Promise; + updateValueById( + _id: string, + value: (ISetting['value'] extends undefined ? never : ISetting['value']) | null, + ): Promise; incrementValueById(_id: ISetting['_id'], value?: number): Promise; @@ -42,6 +45,8 @@ export interface ISettingsModel extends IBaseModel { findSetupWizardSettings(): FindCursor; + findEnterpriseSettings(): FindCursor; + addOptionValueById(_id: ISetting['_id'], option: ISettingSelectOption): Promise; findNotHiddenPublicUpdatedAfter(updatedAt: Date): FindCursor;