Skip to content

Commit

Permalink
[FIX] Remove invalid settings at startup (#27653)
Browse files Browse the repository at this point in the history
  • Loading branch information
KevLehman committed Dec 28, 2022
1 parent ecc06e8 commit a3b8f16
Show file tree
Hide file tree
Showing 5 changed files with 62 additions and 5 deletions.
8 changes: 4 additions & 4 deletions 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';
Expand Down Expand Up @@ -84,9 +84,9 @@ function Section({ groupId, hasReset = true, sectionName, tabName = '', solo, he
)}

<FieldGroup>
{editableSettings.map((setting) => (
<Setting key={setting._id} settingId={setting._id} sectionChanged={changed} />
))}
{editableSettings.map(
(setting) => isSetting(setting) && <Setting key={setting._id} settingId={setting._id} sectionChanged={changed} />,
)}

{children}
</FieldGroup>
Expand Down
7 changes: 6 additions & 1 deletion 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';
Expand All @@ -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(
Expand Down
40 changes: 40 additions & 0 deletions 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();
});
1 change: 1 addition & 0 deletions apps/meteor/server/startup/index.ts
Expand Up @@ -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
Expand Down
11 changes: 11 additions & 0 deletions packages/core-typings/src/ISetting.ts
Expand Up @@ -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;
Expand Down

0 comments on commit a3b8f16

Please sign in to comment.