snapshot of https://github.com/RocketChat/Rocket.Chat.git (develop, 2026-09-16; blobs > 131072B omitted)
Some checks failed
Create issues from TODOs / todos (push) Failing after 3s
Close inactive issues / close-issues (push) Has been cancelled
Deploy GitHub Pages / deploy-preview (push) Has been cancelled
CI / ⚙️ Variables Setup (push) Has been cancelled
CI / ⚙️ Test Guard (push) Has been cancelled
CI / 🚀 Notify external services - draft (push) Has been cancelled
CI / 📦 Build Packages (push) Has been cancelled
CI / 📦 Meteor Build (coverage) (push) Has been cancelled
CI / 📦 Meteor Build (production) (push) Has been cancelled
CI / 🚢 Build Docker (amd64, [account-service presence-service omnichannel-transcript-service], ${{ (github.event_name != 'release' && github.ref != 'refs/heads/develop') && 'coverage' || 'production' }}) (push) Has been cancelled
CI / 🚢 Build Docker (amd64, [account-service presence-service omnichannel-transcript-service], fips) (push) Has been cancelled
CI / 🚢 Build Docker (amd64, [authorization-service queue-worker-service ddp-streamer-service], ${{ (github.event_name != 'release' && github.ref != 'refs/heads/develop') && 'coverage' || 'production' }}) (push) Has been cancelled
CI / 🚢 Build Docker (amd64, [authorization-service queue-worker-service ddp-streamer-service], fips) (push) Has been cancelled
CI / 🚢 Build Docker (amd64, [rocketchat], ${{ (github.event_name != 'release' && github.ref != 'refs/heads/develop') && 'coverage' || 'production' }}) (push) Has been cancelled
CI / 🚢 Build Docker (amd64, [rocketchat], coverage) (push) Has been cancelled
CI / 🚢 Build Docker (amd64, [rocketchat], fips) (push) Has been cancelled
CI / 🚢 Build Docker (arm64, [account-service presence-service omnichannel-transcript-service], ${{ (github.event_name != 'release' && github.ref != 'refs/heads/develop') && 'coverage' || 'production' }}) (push) Has been cancelled
CI / 🚢 Build Docker (arm64, [authorization-service queue-worker-service ddp-streamer-service], ${{ (github.event_name != 'release' && github.ref != 'refs/heads/develop') && 'coverage' || 'production' }}) (push) Has been cancelled
CI / 🚢 Build Docker (arm64, [rocketchat], ${{ (github.event_name != 'release' && github.ref != 'refs/heads/develop') && 'coverage' || 'production' }}) (push) Has been cancelled
CI / 🚢 Build Docker (arm64, [rocketchat], coverage) (push) Has been cancelled
CI / 🚢 Publish Docker Images (ghcr.io) (push) Has been cancelled
CI / 📦 Track Image Sizes (push) Has been cancelled
CI / 🔎 Code Check - Action Lint (push) Has been cancelled
CI / 🔎 Code Check (push) Has been cancelled
CI / 🔨 Test Storybook (push) Has been cancelled
CI / 🔨 Test Unit (push) Has been cancelled
CI / 🔨 Test API (CE) (push) Has been cancelled
CI / 🔨 Test API Livechat (CE) (push) Has been cancelled
CI / 🔨 Test UI (CE) (push) Has been cancelled
CI / 🔨 Test API (EE) (push) Has been cancelled
CI / 🔨 Test API Livechat (EE) (push) Has been cancelled
CI / 🔨 Test API Apps (deno-runtime - EE) (push) Has been cancelled
CI / 🔨 Test UI (EE) (push) Has been cancelled
CI / 🔨 Test API (FIPS) (push) Has been cancelled
CI / 🔨 Test API Livechat (FIPS) (push) Has been cancelled
CI / 🔨 Test UI (FIPS) (push) Has been cancelled
CI / 🔨 Test Federation Matrix (push) Has been cancelled
CI / 📊 Report Coverage (push) Has been cancelled
CI / ✅ Tests Done (push) Has been cancelled
CI / 🚀 Publish build assets (push) Has been cancelled
CI / 🚀 Publish Docker Images (DockerHub) (push) Has been cancelled
CI / 🚀 Notify external services (push) Has been cancelled
CI / Update Version Durability (push) Has been cancelled
Release / Release (push) Has been cancelled
Auto-close duplicate issues / auto-close-duplicates (push) Failing after 1s
Code scanning - action / CodeQL-Build (push) Failing after 1s
Some checks failed
Create issues from TODOs / todos (push) Failing after 3s
Close inactive issues / close-issues (push) Has been cancelled
Deploy GitHub Pages / deploy-preview (push) Has been cancelled
CI / ⚙️ Variables Setup (push) Has been cancelled
CI / ⚙️ Test Guard (push) Has been cancelled
CI / 🚀 Notify external services - draft (push) Has been cancelled
CI / 📦 Build Packages (push) Has been cancelled
CI / 📦 Meteor Build (coverage) (push) Has been cancelled
CI / 📦 Meteor Build (production) (push) Has been cancelled
CI / 🚢 Build Docker (amd64, [account-service presence-service omnichannel-transcript-service], ${{ (github.event_name != 'release' && github.ref != 'refs/heads/develop') && 'coverage' || 'production' }}) (push) Has been cancelled
CI / 🚢 Build Docker (amd64, [account-service presence-service omnichannel-transcript-service], fips) (push) Has been cancelled
CI / 🚢 Build Docker (amd64, [authorization-service queue-worker-service ddp-streamer-service], ${{ (github.event_name != 'release' && github.ref != 'refs/heads/develop') && 'coverage' || 'production' }}) (push) Has been cancelled
CI / 🚢 Build Docker (amd64, [authorization-service queue-worker-service ddp-streamer-service], fips) (push) Has been cancelled
CI / 🚢 Build Docker (amd64, [rocketchat], ${{ (github.event_name != 'release' && github.ref != 'refs/heads/develop') && 'coverage' || 'production' }}) (push) Has been cancelled
CI / 🚢 Build Docker (amd64, [rocketchat], coverage) (push) Has been cancelled
CI / 🚢 Build Docker (amd64, [rocketchat], fips) (push) Has been cancelled
CI / 🚢 Build Docker (arm64, [account-service presence-service omnichannel-transcript-service], ${{ (github.event_name != 'release' && github.ref != 'refs/heads/develop') && 'coverage' || 'production' }}) (push) Has been cancelled
CI / 🚢 Build Docker (arm64, [authorization-service queue-worker-service ddp-streamer-service], ${{ (github.event_name != 'release' && github.ref != 'refs/heads/develop') && 'coverage' || 'production' }}) (push) Has been cancelled
CI / 🚢 Build Docker (arm64, [rocketchat], ${{ (github.event_name != 'release' && github.ref != 'refs/heads/develop') && 'coverage' || 'production' }}) (push) Has been cancelled
CI / 🚢 Build Docker (arm64, [rocketchat], coverage) (push) Has been cancelled
CI / 🚢 Publish Docker Images (ghcr.io) (push) Has been cancelled
CI / 📦 Track Image Sizes (push) Has been cancelled
CI / 🔎 Code Check - Action Lint (push) Has been cancelled
CI / 🔎 Code Check (push) Has been cancelled
CI / 🔨 Test Storybook (push) Has been cancelled
CI / 🔨 Test Unit (push) Has been cancelled
CI / 🔨 Test API (CE) (push) Has been cancelled
CI / 🔨 Test API Livechat (CE) (push) Has been cancelled
CI / 🔨 Test UI (CE) (push) Has been cancelled
CI / 🔨 Test API (EE) (push) Has been cancelled
CI / 🔨 Test API Livechat (EE) (push) Has been cancelled
CI / 🔨 Test API Apps (deno-runtime - EE) (push) Has been cancelled
CI / 🔨 Test UI (EE) (push) Has been cancelled
CI / 🔨 Test API (FIPS) (push) Has been cancelled
CI / 🔨 Test API Livechat (FIPS) (push) Has been cancelled
CI / 🔨 Test UI (FIPS) (push) Has been cancelled
CI / 🔨 Test Federation Matrix (push) Has been cancelled
CI / 📊 Report Coverage (push) Has been cancelled
CI / ✅ Tests Done (push) Has been cancelled
CI / 🚀 Publish build assets (push) Has been cancelled
CI / 🚀 Publish Docker Images (DockerHub) (push) Has been cancelled
CI / 🚀 Notify external services (push) Has been cancelled
CI / Update Version Durability (push) Has been cancelled
Release / Release (push) Has been cancelled
Auto-close duplicate issues / auto-close-duplicates (push) Failing after 1s
Code scanning - action / CodeQL-Build (push) Failing after 1s
This commit is contained in:
17
apps/meteor/server/settings/functions/convertValue.ts
Normal file
17
apps/meteor/server/settings/functions/convertValue.ts
Normal file
@@ -0,0 +1,17 @@
|
||||
import type { ISetting, SettingValue } from '@rocket.chat/core-typings';
|
||||
|
||||
export const convertValue = (value: 'true' | 'false' | string, type: ISetting['type']): SettingValue => {
|
||||
if (value.toLowerCase() === 'true') {
|
||||
return true;
|
||||
}
|
||||
if (value.toLowerCase() === 'false') {
|
||||
return false;
|
||||
}
|
||||
if (type === 'int') {
|
||||
return parseInt(value);
|
||||
}
|
||||
if (type === 'multiSelect') {
|
||||
return JSON.parse(value);
|
||||
}
|
||||
return value;
|
||||
};
|
||||
46
apps/meteor/server/settings/functions/getSettingDefaults.ts
Normal file
46
apps/meteor/server/settings/functions/getSettingDefaults.ts
Normal file
@@ -0,0 +1,46 @@
|
||||
import type { ISetting, ISettingColor } from '@rocket.chat/core-typings';
|
||||
import { isSettingColor, isSettingRange } from '@rocket.chat/core-typings';
|
||||
|
||||
export const getSettingDefaults = (
|
||||
setting: Partial<ISetting> & Pick<ISetting, '_id' | 'value' | 'type'>,
|
||||
blockedSettings: Set<string> = new Set(),
|
||||
hiddenSettings: Set<string> = new Set(),
|
||||
wizardRequiredSettings: Set<string> = new Set(),
|
||||
): ISetting => {
|
||||
const { _id, value, sorter, ...data } = setting;
|
||||
|
||||
const options = Object.fromEntries(Object.entries(data).filter(([, value]) => value !== undefined)) as Partial<typeof data>;
|
||||
|
||||
return {
|
||||
_id,
|
||||
value,
|
||||
packageValue: value,
|
||||
valueSource: 'packageValue',
|
||||
secret: false,
|
||||
enterprise: false,
|
||||
i18nDescription: `${_id}_Description`,
|
||||
autocomplete: true,
|
||||
sorter: sorter || 0,
|
||||
ts: new Date(),
|
||||
createdAt: new Date(),
|
||||
_updatedAt: options._updatedAt ?? new Date(),
|
||||
...options,
|
||||
...(options.enableQuery ? { enableQuery: JSON.stringify(options.enableQuery) } : undefined),
|
||||
...(options.validation ? { validation: JSON.stringify(options.validation) } : undefined),
|
||||
i18nLabel: options.i18nLabel || _id,
|
||||
hidden: options.hidden || hiddenSettings.has(_id),
|
||||
blocked: options.blocked || blockedSettings.has(_id),
|
||||
requiredOnWizard: options.requiredOnWizard || wizardRequiredSettings.has(_id),
|
||||
type: options.type || 'string',
|
||||
env: options.env || false,
|
||||
public: options.public || false,
|
||||
...(options.displayQuery ? { displayQuery: JSON.stringify(options.displayQuery) } : undefined),
|
||||
...(isSettingColor(setting as ISetting) && {
|
||||
packageEditor: (setting as ISettingColor).editor,
|
||||
}),
|
||||
...(isSettingRange(setting as ISetting) && {
|
||||
minValue: 0,
|
||||
maxValue: 100,
|
||||
}),
|
||||
};
|
||||
};
|
||||
57
apps/meteor/server/settings/functions/overrideGenerator.ts
Normal file
57
apps/meteor/server/settings/functions/overrideGenerator.ts
Normal file
@@ -0,0 +1,57 @@
|
||||
import type { ISetting, SettingValueMultiSelect, SettingValueRoomPick } from '@rocket.chat/core-typings';
|
||||
|
||||
import { convertValue } from './convertValue';
|
||||
|
||||
const compareSettingsValue = (a: ISetting['value'], b: ISetting['value'], type?: ISetting['type']): boolean => {
|
||||
if (Array.isArray(a) && Array.isArray(b)) {
|
||||
if (type === 'multiSelect') {
|
||||
a = a as SettingValueMultiSelect;
|
||||
b = b as SettingValueMultiSelect;
|
||||
return a.length === b.length && a.every((value, index) => compareSettingsValue(value, (b as SettingValueMultiSelect)[index]));
|
||||
}
|
||||
|
||||
if (type === 'roomPick') {
|
||||
a = a as SettingValueRoomPick;
|
||||
b = b as SettingValueRoomPick;
|
||||
return (
|
||||
a.length === b.length &&
|
||||
a.every((value, index) => {
|
||||
return Object.keys(value).every((key) =>
|
||||
compareSettingsValue(
|
||||
value[key as keyof typeof value],
|
||||
(b as SettingValueRoomPick)[index as keyof typeof b][key as keyof typeof b],
|
||||
),
|
||||
);
|
||||
})
|
||||
);
|
||||
}
|
||||
}
|
||||
return a === b;
|
||||
};
|
||||
|
||||
export const overrideGenerator =
|
||||
(fn: (key: string) => string | undefined) =>
|
||||
(setting: ISetting): ISetting => {
|
||||
const overwriteValue = fn(setting._id);
|
||||
if (overwriteValue === null || overwriteValue === undefined) {
|
||||
return setting;
|
||||
}
|
||||
|
||||
try {
|
||||
const value = convertValue(overwriteValue, setting.type);
|
||||
|
||||
if (compareSettingsValue(value, setting.value, setting.type) && compareSettingsValue(value, setting.processEnvValue, setting.type)) {
|
||||
return setting;
|
||||
}
|
||||
|
||||
return {
|
||||
...setting,
|
||||
value,
|
||||
processEnvValue: value,
|
||||
valueSource: 'processEnvValue',
|
||||
};
|
||||
} catch (error) {
|
||||
console.error(`Error converting value for setting ${setting._id} expected "${setting.type}" type`);
|
||||
return setting;
|
||||
}
|
||||
};
|
||||
3
apps/meteor/server/settings/functions/overrideSetting.ts
Normal file
3
apps/meteor/server/settings/functions/overrideSetting.ts
Normal file
@@ -0,0 +1,3 @@
|
||||
import { overrideGenerator } from './overrideGenerator';
|
||||
|
||||
export const overrideSetting = overrideGenerator((key: string) => process.env[key]);
|
||||
@@ -0,0 +1,3 @@
|
||||
import { overrideGenerator } from './overrideGenerator';
|
||||
|
||||
export const overwriteSetting = overrideGenerator((key: string) => process.env[`OVERWRITE_SETTING_${key}`]);
|
||||
10
apps/meteor/server/settings/functions/settingSchemas.ts
Normal file
10
apps/meteor/server/settings/functions/settingSchemas.ts
Normal file
@@ -0,0 +1,10 @@
|
||||
import type { ISetting } from '@rocket.chat/core-typings';
|
||||
import { ajv } from '@rocket.chat/rest-typings';
|
||||
|
||||
const schemas = new Map<ISetting['_id'], ReturnType<typeof ajv.compile>>();
|
||||
|
||||
export const registerSettingSchema = (_id: ISetting['_id'], schema: Record<string, unknown>): void => {
|
||||
schemas.set(_id, ajv.compile(schema));
|
||||
};
|
||||
|
||||
export const getSettingSchemaValidator = (_id: ISetting['_id']): ReturnType<typeof ajv.compile> | undefined => schemas.get(_id);
|
||||
112
apps/meteor/server/settings/functions/settings.mocks.ts
Normal file
112
apps/meteor/server/settings/functions/settings.mocks.ts
Normal file
@@ -0,0 +1,112 @@
|
||||
import type { ISetting } from '@rocket.chat/core-typings';
|
||||
|
||||
import type { ICachedSettings } from '../CachedSettings';
|
||||
|
||||
type Dictionary = {
|
||||
[index: string]: any;
|
||||
};
|
||||
|
||||
class SettingsClass {
|
||||
settings: ICachedSettings;
|
||||
|
||||
private delay = 0;
|
||||
|
||||
setDelay(delay: number): void {
|
||||
this.delay = delay;
|
||||
}
|
||||
|
||||
find(): any[] {
|
||||
return [];
|
||||
}
|
||||
|
||||
public data = new Map<string, Dictionary>();
|
||||
|
||||
public upsertCalls = 0;
|
||||
|
||||
public insertCalls = 0;
|
||||
|
||||
private checkQueryMatch(key: string, data: Dictionary, queryValue: any): boolean {
|
||||
if (typeof queryValue === 'object') {
|
||||
if (queryValue.$exists !== undefined) {
|
||||
return (data.hasOwnProperty(key) && data[key] !== undefined) === queryValue.$exists;
|
||||
}
|
||||
}
|
||||
|
||||
return queryValue === data[key];
|
||||
}
|
||||
|
||||
findOne(query: Dictionary): any {
|
||||
return [...this.data.values()].find((data) => Object.entries(query).every(([key, value]) => this.checkQueryMatch(key, data, value)));
|
||||
}
|
||||
|
||||
insertOne(doc: any): void {
|
||||
this.data.set(doc._id, doc);
|
||||
|
||||
this.settings.set(doc);
|
||||
this.insertCalls++;
|
||||
}
|
||||
|
||||
updateOne(query: any, update: any, options?: any): void {
|
||||
const existent = this.findOne(query);
|
||||
|
||||
const data = { ...existent, ...query, ...update, ...update.$set };
|
||||
|
||||
if (!existent) {
|
||||
Object.assign(data, update.$setOnInsert);
|
||||
}
|
||||
|
||||
if (update.$unset) {
|
||||
Object.keys(update.$unset).forEach((key) => {
|
||||
delete data[key];
|
||||
});
|
||||
}
|
||||
|
||||
const modifiers = ['$set', '$setOnInsert', '$unset'];
|
||||
|
||||
modifiers.forEach((key) => {
|
||||
delete data[key];
|
||||
});
|
||||
|
||||
if (options?.upsert === true && !modifiers.some((key) => Object.keys(update).includes(key))) {
|
||||
throw new Error('Invalid upsert');
|
||||
}
|
||||
|
||||
if (this.delay) {
|
||||
setTimeout(() => {
|
||||
// console.log(query, data);
|
||||
this.data.set(query._id, data);
|
||||
|
||||
// Can't import before the mock command on end of this file!
|
||||
|
||||
this.settings.set(data);
|
||||
}, this.delay);
|
||||
} else {
|
||||
this.data.set(query._id, data);
|
||||
// Can't import before the mock command on end of this file!
|
||||
|
||||
this.settings.set(data);
|
||||
}
|
||||
|
||||
this.upsertCalls++;
|
||||
}
|
||||
|
||||
findOneAndUpdate({ _id }: { _id: string }, value: any, options?: any) {
|
||||
this.updateOne({ _id }, value, options);
|
||||
return { value: this.findOne({ _id }) };
|
||||
}
|
||||
|
||||
updateValueById(id: string, value: any): void {
|
||||
this.data.set(id, { ...this.data.get(id), value });
|
||||
// Can't import before the mock command on end of this file!
|
||||
|
||||
if (this.delay) {
|
||||
setTimeout(() => {
|
||||
this.settings.set(this.data.get(id) as ISetting);
|
||||
}, this.delay);
|
||||
} else {
|
||||
this.settings.set(this.data.get(id) as ISetting);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export const Settings = new SettingsClass();
|
||||
59
apps/meteor/server/settings/functions/validateSetting.ts
Normal file
59
apps/meteor/server/settings/functions/validateSetting.ts
Normal file
@@ -0,0 +1,59 @@
|
||||
import { isActionSettingWithEndpoint, type ISetting } from '@rocket.chat/core-typings';
|
||||
|
||||
export const validateSetting = <T extends ISetting>(_id: T['_id'], type: T['type'], value: T['value'] | unknown): boolean => {
|
||||
switch (type) {
|
||||
case 'asset':
|
||||
if (typeof value !== 'object') {
|
||||
throw new Error(`Setting ${_id} is of type ${type} but got ${typeof value}`);
|
||||
}
|
||||
break;
|
||||
case 'action':
|
||||
if (typeof value !== 'string' && !isActionSettingWithEndpoint(value as never)) {
|
||||
throw new Error(`Setting ${_id} is of type ${type} but got ${typeof value}`);
|
||||
}
|
||||
break;
|
||||
case 'string':
|
||||
case 'relativeUrl':
|
||||
case 'password':
|
||||
case 'language':
|
||||
case 'color':
|
||||
case 'font':
|
||||
case 'code':
|
||||
case 'roomPick':
|
||||
case 'group':
|
||||
if (typeof value !== 'string') {
|
||||
throw new Error(`Setting ${_id} is of type ${type} but got ${typeof value}`);
|
||||
}
|
||||
break;
|
||||
case 'boolean':
|
||||
if (typeof value !== 'boolean') {
|
||||
throw new Error(`Setting ${_id} is of type boolean but got ${typeof value}`);
|
||||
}
|
||||
break;
|
||||
case 'int':
|
||||
if (typeof value !== 'number') {
|
||||
throw new Error(`Setting ${_id} is of type int but got ${typeof value}`);
|
||||
}
|
||||
break;
|
||||
case 'multiSelect':
|
||||
if (!Array.isArray(value)) {
|
||||
throw new Error(`Setting ${_id} is of type array but got ${typeof value}`);
|
||||
}
|
||||
break;
|
||||
case 'select':
|
||||
case 'lookup':
|
||||
if (typeof value !== 'string' && typeof value !== 'number') {
|
||||
throw new Error(`Setting ${_id} is of type ${type} but got ${typeof value}`);
|
||||
}
|
||||
break;
|
||||
case 'date':
|
||||
if (!(value instanceof Date)) {
|
||||
throw new Error(`Setting ${_id} is of type date but got ${typeof value}`);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
return true;
|
||||
}
|
||||
|
||||
return true;
|
||||
};
|
||||
@@ -0,0 +1,14 @@
|
||||
import type { ISetting, SettingValidationRule } from '@rocket.chat/core-typings';
|
||||
|
||||
export const positiveOrDisabled = (): SettingValidationRule => ({
|
||||
query: { $or: [{ value: -1 }, { value: { $gte: 1 } }] },
|
||||
});
|
||||
|
||||
export const notGreaterThanSetting = (otherId: ISetting['_id']): SettingValidationRule => ({
|
||||
query: { $or: [{ value: { $lt: 1 } }, { value: { $lte: { $setting: otherId } } }] },
|
||||
appliesWhen: { _id: otherId, value: { $gte: 1 } },
|
||||
});
|
||||
|
||||
export const notLowerThanSetting = (otherId: ISetting['_id']): SettingValidationRule => ({
|
||||
query: { $or: [{ value: { $lt: 1 } }, { value: { $gte: { $setting: otherId } } }] },
|
||||
});
|
||||
Reference in New Issue
Block a user