From a3b8f16fccb282003502ec48deeda1b46893ba80 Mon Sep 17 00:00:00 2001 From: Kevin Aleman Date: Wed, 28 Dec 2022 08:28:46 -0600 Subject: [PATCH] [FIX] Remove invalid settings at startup (#27653) --- .../client/views/admin/settings/Section.tsx | 8 ++-- .../client/views/admin/settings/Setting.tsx | 7 +++- apps/meteor/server/lib/settingsRegenerator.ts | 40 +++++++++++++++++++ apps/meteor/server/startup/index.ts | 1 + packages/core-typings/src/ISetting.ts | 11 +++++ 5 files changed, 62 insertions(+), 5 deletions(-) create mode 100644 apps/meteor/server/lib/settingsRegenerator.ts diff --git a/apps/meteor/client/views/admin/settings/Section.tsx b/apps/meteor/client/views/admin/settings/Section.tsx index fe65824cd9a9..12ee06c9ad8f 100644 --- a/apps/meteor/client/views/admin/settings/Section.tsx +++ b/apps/meteor/client/views/admin/settings/Section.tsx @@ -1,4 +1,4 @@ -import { isSettingColor } from '@rocket.chat/core-typings'; +import { isSetting, isSettingColor } from '@rocket.chat/core-typings'; import { Accordion, Box, Button, FieldGroup } from '@rocket.chat/fuselage'; import { useMutableCallback } from '@rocket.chat/fuselage-hooks'; import type { TranslationKey } from '@rocket.chat/ui-contexts'; @@ -84,9 +84,9 @@ function Section({ groupId, hasReset = true, sectionName, tabName = '', solo, he )} - {editableSettings.map((setting) => ( - - ))} + {editableSettings.map( + (setting) => isSetting(setting) && , + )} {children} diff --git a/apps/meteor/client/views/admin/settings/Setting.tsx b/apps/meteor/client/views/admin/settings/Setting.tsx index f2e6878a038e..b36be24f8598 100644 --- a/apps/meteor/client/views/admin/settings/Setting.tsx +++ b/apps/meteor/client/views/admin/settings/Setting.tsx @@ -1,5 +1,5 @@ import type { ISettingColor, SettingEditor, SettingValue } from '@rocket.chat/core-typings'; -import { isSettingColor } from '@rocket.chat/core-typings'; +import { isSettingColor, isSetting } from '@rocket.chat/core-typings'; import { useDebouncedCallback } from '@rocket.chat/fuselage-hooks'; import { useSettingStructure, useTranslation, useAbsoluteUrl } from '@rocket.chat/ui-contexts'; import type { ReactElement } from 'react'; @@ -25,6 +25,11 @@ function Setting({ className = undefined, settingId, sectionChanged }: SettingPr throw new Error(`Setting ${settingId} not found`); } + // Checks if setting has at least required fields before doing anything + if (!isSetting(setting)) { + throw new Error(`Setting ${settingId} is not valid`); + } + const dispatch = useEditableSettingsDispatch(); const update = useDebouncedCallback( diff --git a/apps/meteor/server/lib/settingsRegenerator.ts b/apps/meteor/server/lib/settingsRegenerator.ts new file mode 100644 index 000000000000..954a896343cd --- /dev/null +++ b/apps/meteor/server/lib/settingsRegenerator.ts @@ -0,0 +1,40 @@ +// Validates settings on DB are correct on structure +// And deletes invalid ones +import { Meteor } from 'meteor/meteor'; +import { Settings } from '@rocket.chat/models'; + +import { Logger } from './logger/Logger'; + +// Validates settings on DB are correct on structure by matching the ones missing all the required fields +const logger = new Logger('SettingsRegenerator'); +export async function settingsRegenerator() { + const invalidSettings = await Settings.find( + { + // Putting the $and explicit to ensure it's "intentional" + $and: [ + { value: { $exists: false } }, + { type: { $exists: false } }, + { public: { $exists: false } }, + { packageValue: { $exists: false } }, + { blocked: { $exists: false } }, + { sorter: { $exists: false } }, + { i18nLabel: { $exists: false } }, + ], + }, + { projection: { _id: 1 } }, + ).toArray(); + + if (invalidSettings.length > 0) { + logger.warn({ + msg: 'Invalid settings found on DB. Deleting them.', + settings: invalidSettings.map(({ _id }) => _id), + }); + await Settings.deleteMany({ _id: { $in: invalidSettings.map(({ _id }) => _id) } }); + } else { + logger.info('No invalid settings found on DB.'); + } +} + +Meteor.startup(async () => { + await settingsRegenerator(); +}); diff --git a/apps/meteor/server/startup/index.ts b/apps/meteor/server/startup/index.ts index cb2ef39b7a9e..86385bd15623 100644 --- a/apps/meteor/server/startup/index.ts +++ b/apps/meteor/server/startup/index.ts @@ -9,6 +9,7 @@ import './coreApps'; import './presenceTroubleshoot'; import '../hooks'; import '../lib/rooms/roomTypes'; +import '../lib/settingsRegenerator'; import { isRunningMs } from '../lib/isRunningMs'; // only starts network broker if running in micro services mode diff --git a/packages/core-typings/src/ISetting.ts b/packages/core-typings/src/ISetting.ts index 99b7e2cc27cd..f18d303df5b4 100644 --- a/packages/core-typings/src/ISetting.ts +++ b/packages/core-typings/src/ISetting.ts @@ -132,6 +132,17 @@ export interface ISettingDate extends ISettingBase { value: Date; } +// Checks if setting has at least the required properties +export const isSetting = (setting: any): setting is ISetting => + '_id' in setting && + 'type' in setting && + 'public' in setting && + 'value' in setting && + 'packageValue' in setting && + 'blocked' in setting && + 'sorter' in setting && + 'i18nLabel' in setting; + export const isDateSetting = (setting: ISetting): setting is ISettingDate => setting.type === 'date'; export const isSettingEnterprise = (setting: ISettingBase): setting is ISettingEnterprise => setting.enterprise === true;