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

This commit is contained in:
upstream-mirror
2026-09-16 01:21:02 +05:00
commit 30810eecd7
10161 changed files with 987110 additions and 0 deletions

View File

@@ -0,0 +1,45 @@
import './minimumVersion';
import './v293';
import './v294';
import './v295';
import './v296';
import './v297';
import './v298';
import './v299';
import './v300';
import './v301';
import './v303';
import './v304';
import './v305';
import './v306';
import './v307';
import './v308';
import './v309';
import './v310';
import './v311';
import './v312';
import './v313';
import './v314';
import './v315';
import './v316';
import './v317';
import './v318';
import './v319';
import './v320';
import './v321';
import './v322';
import './v323';
import './v324';
import './v325';
import './v326';
import './v327';
import './v328';
import './v329';
import './v330';
import './v331';
import './v332';
import './v333';
import './v334';
import './v335';
export * from './xrun';

View File

@@ -0,0 +1,20 @@
import { addMigration } from '../../lib/migrations';
// this was the first migration added on version 6.0, so if this needs to run,
// it means the server was never updated to 6.x, which is not supported.
addMigration({
version: 292,
up() {
throw new Error(
[
'UPGRADE NOT SUPPORTED!',
'',
`It seems you're trying to upgrade from an unsupported version!`,
'',
'To be able to update to version 7.x.y you need to update to version 6.x first.',
'',
'Read more: https://go.rocket.chat/i/how-to-upgrade',
].join('\n'),
);
},
});

View File

@@ -0,0 +1,9 @@
import { upsertPermissions } from '../../lib/authorization/upsertPermissions';
import { addMigration } from '../../lib/migrations';
addMigration({
version: 293,
async up() {
await upsertPermissions();
},
});

View File

@@ -0,0 +1,37 @@
import { Apps } from '@rocket.chat/apps';
import type { IAppStorageItem } from '@rocket.chat/apps/dist/server/storage/IAppStorageItem';
import { addMigration } from '../../lib/migrations';
addMigration({
version: 294,
async up() {
if (!Apps.self) {
throw new Error('Apps Orchestrator not registered.');
}
Apps.initialize();
const sigMan = Apps.getManager().getSignatureManager();
const appsStorage = Apps.getStorage();
const apps = await appsStorage.retrieveAll();
for (const app of apps.values()) {
if (app.installationSource && app.signature) {
continue;
}
const updatedApp = {
...app,
migrated: true,
installationSource: 'marketplaceInfo' in app ? 'marketplace' : 'private',
} as IAppStorageItem;
await appsStorage.updatePartialAndReturnDocument({
...updatedApp,
signature: await sigMan.signApp(updatedApp),
});
}
},
});

View File

@@ -0,0 +1,45 @@
import type { ISetting } from '@rocket.chat/core-typings';
import { Settings } from '@rocket.chat/models';
import { SystemLogger } from '../../lib/logger/system';
import { addMigration } from '../../lib/migrations';
import { updateAuditedBySystem } from '../../settings/lib/auditedSettingUpdates';
addMigration({
version: 295,
name: 'Change old "LDAP_Background_Sync_Interval" and "CROWD_Sync_Interval" to a pre-defined values instead of accept any input from the user',
async up() {
const oldLdapDefault = 'Every 24 hours';
const oldCrowdDefault = 'Every 60 mins';
const newLdapDefault = 'every_24_hours';
const newCrowdDefault = 'every_1_hours';
const ldapSyncInterval = await Settings.findOneById<Pick<ISetting, 'value'>>('LDAP_Background_Sync_Interval', {
projection: { value: 1 },
});
const crowdSyncInterval = await Settings.findOneById<Pick<ISetting, 'value'>>('CROWD_Sync_Interval', { projection: { value: 1 } });
// update setting values
await updateAuditedBySystem({
reason: 'Migration 295',
})(Settings.updateValueById, 'LDAP_Background_Sync_Interval', newLdapDefault);
await updateAuditedBySystem({
reason: 'Migration 295',
})(Settings.updateValueById, 'CROWD_Sync_Interval', newCrowdDefault);
// notify user about the changes if the value was different from the default
if (ldapSyncInterval && ldapSyncInterval.value !== oldLdapDefault) {
SystemLogger.warn(
`The value of the setting 'LDAP background synchronization interval' has changed from "${ldapSyncInterval.value}" to "${newLdapDefault}". Please review your settings.`,
);
}
if (crowdSyncInterval && crowdSyncInterval.value !== oldCrowdDefault) {
SystemLogger.warn(
`The value of the setting 'CROWD background synchronization interval' has changed from "${crowdSyncInterval.value}" to "${newCrowdDefault}". Please review your settings.`,
);
}
},
});

View File

@@ -0,0 +1,22 @@
import { Settings } from '@rocket.chat/models';
import { SystemLogger } from '../../lib/logger/system';
import { addMigration } from '../../lib/migrations';
import { settings } from '../../settings';
addMigration({
version: 296,
name: 'Reset the default value of Login Terms setting and replace by empty string',
async up() {
const oldLoginTermsValue =
'By proceeding you are agreeing to our <a href="terms-of-service">Terms of Service</a>, <a href="privacy-policy">Privacy Policy</a> and <a href="legal-notice">Legal Notice</a>.';
const loginTermsValue = settings.get('Layout_Login_Terms');
if (loginTermsValue === oldLoginTermsValue) {
// TODO: audit
await Settings.updateOne({ _id: 'Layout_Login_Terms' }, { $set: { value: '', packageValue: '' } });
SystemLogger.warn(`The default value of the setting 'Login Terms' has changed to an empty string. Please review your settings.`);
}
},
});

View File

@@ -0,0 +1,9 @@
import { upsertPermissions } from '../../lib/authorization/upsertPermissions';
import { addMigration } from '../../lib/migrations';
addMigration({
version: 297,
async up() {
await upsertPermissions();
},
});

View File

@@ -0,0 +1,20 @@
import { Integrations } from '@rocket.chat/models';
import { addMigration } from '../../lib/migrations';
addMigration({
version: 298,
name: 'Set overrideDestinationChannelEnabled for all incoming webhook integrations',
async up() {
await Integrations.updateMany(
{
type: 'webhook-incoming',
},
{
$set: {
overrideDestinationChannelEnabled: true,
},
},
);
},
});

View File

@@ -0,0 +1,11 @@
import { Settings } from '@rocket.chat/models';
import { addMigration } from '../../lib/migrations';
addMigration({
version: 299,
name: 'Add public field to existing custom OAuth settings',
async up() {
await Settings.updateMany({ _id: /^Accounts_OAuth_Custom.+/, group: 'OAuth' }, { $set: { public: false } });
},
});

View File

@@ -0,0 +1,27 @@
import { Settings } from '@rocket.chat/models';
import { addMigration } from '../../lib/migrations';
import { settingsRegistry } from '../../settings';
addMigration({
version: 300,
async up() {
const customOauthServices = await Settings.find({ _id: /Accounts_OAuth_Custom-[^-]+$/ }, { projection: { _id: 1 } }).toArray();
const serviceNames = customOauthServices.map(({ _id }) => _id.replace('Accounts_OAuth_Custom-', ''));
for await (const serviceName of serviceNames) {
await settingsRegistry.add(`Accounts_OAuth_Custom-${serviceName}-merge_users_distinct_services`, false, {
type: 'boolean',
group: 'OAuth',
section: `Custom OAuth: ${serviceName}`,
i18nLabel: 'Accounts_OAuth_Custom_Merge_Users_Distinct_Services',
i18nDescription: 'Accounts_OAuth_Custom_Merge_Users_Distinct_Services_Description',
enableQuery: {
_id: `Accounts_OAuth_Custom-${serviceName}-merge_users`,
value: true,
},
persistent: true,
});
}
},
});

View File

@@ -0,0 +1,10 @@
import { Permissions } from '@rocket.chat/models';
import { addMigration } from '../../lib/migrations';
addMigration({
version: 301,
async up() {
await Permissions.updateOne({ _id: 'call-management', roles: { $ne: 'user' } }, { $addToSet: { roles: 'user' } });
},
});

View File

@@ -0,0 +1,16 @@
import { Users } from '@rocket.chat/models';
import { addMigration } from '../../lib/migrations';
addMigration({
version: 303,
name: 'Add roles field and set empty for users without it',
async up() {
await Users.updateMany(
{
roles: { $exists: false },
},
{ $set: { roles: [] } },
);
},
});

View File

@@ -0,0 +1,22 @@
import { Analytics } from '@rocket.chat/models';
import { addMigration } from '../../lib/migrations';
addMigration({
version: 304,
name: 'Drop wrong index from analytics collection',
async up() {
const indexes = await Analytics.col.indexes();
if (
indexes.find(
(index) => index.name === 'room._id_1_date_1' && index.partialFilterExpression && index.partialFilterExpression.type === 'rooms',
)
) {
return;
}
await Analytics.col.dropIndex('room._id_1_date_1');
await Analytics.createIndexes();
},
});

View File

@@ -0,0 +1,11 @@
import { Permissions } from '@rocket.chat/models';
import { addMigration } from '../../lib/migrations';
addMigration({
version: 305,
name: 'Remove unused view-history permission',
async up() {
await Permissions.deleteOne({ _id: 'view-history' });
},
});

View File

@@ -0,0 +1,22 @@
import { Rooms } from '@rocket.chat/models';
import { addMigration } from '../../lib/migrations';
addMigration({
version: 306,
name: 'Adds missing ignoreThreads parameter for old rooms with retention policy overridden',
async up() {
await Rooms.updateMany(
{
'retention.enabled': true,
'retention.overrideGlobal': true,
'retention.ignoreThreads': { $exists: false },
},
{
$set: {
'retention.ignoreThreads': false,
},
},
);
},
});

View File

@@ -0,0 +1,38 @@
import { Apps } from '@rocket.chat/apps';
import type { IAppStorageItem } from '@rocket.chat/apps/dist/server/storage/IAppStorageItem';
import { License } from '@rocket.chat/license';
import { addMigration } from '../../lib/migrations';
addMigration({
version: 307,
name: "Mark all installed private apps as 'migrated'",
async up() {
const isEE = License.hasValidLicense();
if (isEE) {
return;
}
if (!Apps.self) {
throw new Error('Apps Orchestrator not registered.');
}
Apps.initialize();
const sigMan = Apps.getManager().getSignatureManager();
const appsStorage = Apps.getStorage();
const apps = await appsStorage.retrieveAllPrivate();
for (const app of apps.values()) {
const updatedApp = {
...app,
migrated: true,
} as IAppStorageItem;
await appsStorage.updatePartialAndReturnDocument({
...updatedApp,
signature: await sigMan.signApp(updatedApp),
});
}
},
});

View File

@@ -0,0 +1,32 @@
import type { ISetting } from '@rocket.chat/core-typings';
import { Settings } from '@rocket.chat/models';
import { isValidCron } from 'cron-validator';
import { addMigration } from '../../lib/migrations';
addMigration({
version: 308,
name: 'Update packageValue from LDAP interval settings',
async up() {
const newAvatarSyncPackageValue = '0 0 * * *';
const newAutoLogoutPackageValue = '*/5 * * * *';
const ldapAvatarSyncInterval = await Settings.findOneById<Pick<ISetting, 'value'>>('LDAP_Background_Sync_Avatars_Interval', {
projection: { value: 1 },
});
const ldapAutoLogoutInterval = await Settings.findOneById<Pick<ISetting, 'value'>>('LDAP_Sync_AutoLogout_Interval', {
projection: { value: 1 },
});
const isValidAvatarSyncInterval = ldapAvatarSyncInterval && isValidCron(ldapAvatarSyncInterval.value as string);
const isValidAutoLogoutInterval = ldapAutoLogoutInterval && isValidCron(ldapAutoLogoutInterval.value as string);
// TODO audit
await Settings.updateOne(
{ _id: 'LDAP_Background_Sync_Avatars_Interval' },
{ $set: { packageValue: newAvatarSyncPackageValue, ...(!isValidAvatarSyncInterval && { value: newAvatarSyncPackageValue }) } },
);
await Settings.updateOne(
{ _id: 'LDAP_Sync_AutoLogout_Interval' },
{ $set: { packageValue: newAutoLogoutPackageValue, ...(!isValidAutoLogoutInterval && { value: newAutoLogoutPackageValue }) } },
);
},
});

View File

@@ -0,0 +1,11 @@
import { Settings } from '@rocket.chat/models';
import { addMigration } from '../../lib/migrations';
addMigration({
version: 309,
name: 'Remove unused UI_Click_Direct_Message setting',
async up() {
await Settings.removeById('UI_Click_Direct_Message');
},
});

View File

@@ -0,0 +1,29 @@
import type { ISetting } from '@rocket.chat/core-typings';
import { Settings } from '@rocket.chat/models';
import { addMigration } from '../../lib/migrations';
addMigration({
version: 310,
name: 'Update translation key on "Forgot password" e-mail body setting',
async up() {
const forgotPasswordEmail = await Settings.findOneById<Pick<ISetting, 'value'>>('Forgot_Password_Email', { projection: { value: 1 } });
if (!forgotPasswordEmail) {
return;
}
const newPackageValue =
'<h2>{Forgot_password}</h2><p>{Lets_get_you_new_one_}</p><a class="btn" href="[Forgot_Password_Url]">{Reset}</a><p class="advice">{If_you_didnt_ask_for_reset_ignore_this_email}</p>';
// TODO: audit
await Settings.updateOne(
{ _id: 'Forgot_Password_Email' },
{
$set: {
packageValue: newPackageValue,
value: (forgotPasswordEmail.value as string).replace(/{Lets_get_you_new_one}/g, '{Lets_get_you_new_one_}'),
},
},
);
},
});

View File

@@ -0,0 +1,22 @@
import { Settings } from '@rocket.chat/models';
import { addMigration } from '../../lib/migrations';
addMigration({
version: 311,
name: 'Update default behavior of E2E_Enabled_Mentions setting, to allow mentions in encrypted messages by default.',
async up() {
// TODO: audit
await Settings.updateOne(
{
_id: 'E2E_Enabled_Mentions',
},
{
$set: {
value: true,
packageValue: true,
},
},
);
},
});

View File

@@ -0,0 +1,20 @@
import { LivechatRooms, Rooms, Subscriptions, Users } from '@rocket.chat/models';
import { addMigration } from '../../lib/migrations';
addMigration({
version: 312,
async up() {
try {
await Promise.allSettled([
LivechatRooms.col.dropIndex('v.token_1'),
Rooms.col.dropIndex('t_1'),
Subscriptions.col.dropIndex('rid_1'),
Users.col.dropIndex('active_1'),
]);
} catch (error: unknown) {
console.warn('Error dropping redundant indexes, continuing...');
console.warn(error);
}
},
});

View File

@@ -0,0 +1,11 @@
import { Settings } from '@rocket.chat/models';
import { addMigration } from '../../lib/migrations';
addMigration({
version: 313,
name: 'Remove "Call_Center" setting group',
async up() {
await Settings.deleteOne({ _id: 'Call_Center', type: 'group' });
},
});

View File

@@ -0,0 +1,22 @@
import { Settings } from '@rocket.chat/models';
import { addMigration } from '../../lib/migrations';
addMigration({
version: 314,
name: 'Update default behavior of E2E_Allow_Unencrypted_Messages setting, to not allow un-encrypted messages by default.',
async up() {
// TODO: audit
await Settings.updateOne(
{
_id: 'E2E_Allow_Unencrypted_Messages',
},
{
$set: {
value: false,
packageValue: false,
},
},
);
},
});

View File

@@ -0,0 +1,27 @@
import type { IPermission } from '@rocket.chat/core-typings';
import { Permissions } from '@rocket.chat/models';
import { upsertPermissions } from '../../lib/authorization/upsertPermissions';
import { addMigration } from '../../lib/migrations';
addMigration({
version: 315,
name: 'Copy roles from add-team-channel permission to new create-team-channel, create-team-group and move-room-to-team permissions',
async up() {
// Calling upsertPermissions on purpose so that the new permissions are added before the migration runs
await upsertPermissions();
const addTeamChannelPermission = await Permissions.findOneById<Pick<IPermission, 'roles'>>('add-team-channel', {
projection: { roles: 1 },
});
if (addTeamChannelPermission) {
await Permissions.updateMany(
{ _id: { $in: ['create-team-channel', 'create-team-group', 'move-room-to-team'] } },
{ $set: { roles: addTeamChannelPermission.roles } },
);
await Permissions.deleteOne({ _id: 'add-team-channel' });
}
},
});

View File

@@ -0,0 +1,20 @@
import { Settings } from '@rocket.chat/models';
import { addMigration } from '../../lib/migrations';
addMigration({
version: 316,
name: 'Remove Cloud_Workspace_Access_Token and Cloud_Workspace_Access_Token_Expires_At from the settings collection',
async up() {
const accessToken = ((await Settings.getValueById('Cloud_Workspace_Access_Token')) as string) || '';
const expirationDate = ((await Settings.getValueById('Cloud_Workspace_Access_Token_Expires_At')) as Date) || new Date(0);
if (accessToken) {
await Settings.removeById('Cloud_Workspace_Access_Token');
}
if (expirationDate) {
await Settings.removeById('Cloud_Workspace_Access_Token_Expires_At');
}
},
});

View File

@@ -0,0 +1,124 @@
import type { ILoginServiceConfiguration, OAuthConfiguration } from '@rocket.chat/core-typings';
import { Settings, LoginServiceConfiguration } from '@rocket.chat/models';
import { isTruthy } from '@rocket.chat/tools';
import { SystemLogger } from '../../lib/logger/system';
import { addMigration } from '../../lib/migrations';
const newDefaultButtonColor = '#e4e7ea';
const newDefaultButtonLabelColor = '#1f2329';
const settingsToUpdate = [
// button background colors
{ key: 'SAML_Custom_Default_button_color', defaultValue: '#1d74f5', newValue: newDefaultButtonColor },
{ key: 'CAS_button_color', defaultValue: '#1d74f5', newValue: newDefaultButtonColor },
{ key: 'Accounts_OAuth_Nextcloud_button_color', defaultValue: '#0082c9', newValue: newDefaultButtonColor },
{ key: 'Accounts_OAuth_Dolphin_button_color', defaultValue: '#1d74f5', newValue: newDefaultButtonColor },
// button label colors
{ key: 'SAML_Custom_Default_button_label_color', defaultValue: '#1d74f5', newValue: newDefaultButtonLabelColor },
{ key: 'CAS_button_label_color', defaultValue: '#1d74f5', newValue: newDefaultButtonLabelColor },
{ key: 'Accounts_OAuth_Nextcloud_button_label_color', defaultValue: '#1d74f5', newValue: newDefaultButtonLabelColor },
{ key: 'Accounts_OAuth_Dolphin_button_label_color', defaultValue: '#1d74f5', newValue: newDefaultButtonLabelColor },
];
const getSettingValue = async (key: string) => Settings.getValueById(key);
async function updateOAuthServices(): Promise<void> {
const services = await Settings.find({ _id: { $regex: /^(Accounts_OAuth_|Accounts_OAuth_Custom-)[a-z0-9_]+$/i } }).toArray();
const filteredServices = services.filter(({ value }) => typeof value === 'boolean');
for await (const { _id: key, value } of filteredServices) {
if (value !== true) {
continue;
}
let serviceName = key.replace('Accounts_OAuth_', '');
if (serviceName === 'Meteor') {
serviceName = 'meteor-developer';
}
if (/Accounts_OAuth_Custom-/.test(key)) {
serviceName = key.replace('Accounts_OAuth_Custom-', '');
}
const serviceKey = serviceName.toLowerCase();
const data: Partial<ILoginServiceConfiguration & Omit<OAuthConfiguration, '_id'>> = {};
if (/Accounts_OAuth_Custom-/.test(key)) {
data.buttonLabelColor = (await getSettingValue(`${key}-button_label_color`)) as string;
data.buttonColor = (await getSettingValue(`${key}-button_color`)) as string;
}
if (serviceName === 'Nextcloud') {
data.buttonLabelColor = (await getSettingValue('Accounts_OAuth_Nextcloud_button_label_color')) as string;
data.buttonColor = (await getSettingValue('Accounts_OAuth_Nextcloud_button_color')) as string;
}
await LoginServiceConfiguration.createOrUpdateService(serviceKey, data);
}
}
addMigration({
version: 317,
name: 'Change default color of OAuth login services buttons',
async up() {
// TODO: audit
const promises = settingsToUpdate
.map(async ({ key, defaultValue, newValue }) => {
const oldSettingValue = await getSettingValue(key);
if (!oldSettingValue || oldSettingValue !== defaultValue) {
return;
}
SystemLogger.warn({ msg: 'The default value of the setting has changed. Please review your settings.', key, newValue });
return Settings.updateOne({ _id: key }, { $set: { value: newValue } });
})
.filter(isTruthy);
await Promise.all(promises);
const customOAuthButtonColors = await Settings.find({
_id: { $regex: /^Accounts_OAuth_Custom-[a-zA-Z0-9_-]+-button_color$/ },
}).toArray();
const customOAuthButtonLabelColors = await Settings.find({
_id: { $regex: /^Accounts_OAuth_Custom-[a-zA-Z0-9_-]+-button_label_color$/ },
}).toArray();
const buttonColorPromises = customOAuthButtonColors
.map(({ _id, value, packageValue }) => {
if (packageValue !== value) {
return;
}
SystemLogger.warn({
msg: 'The default value of the custom setting has changed. Please review your settings.',
_id,
newValue: newDefaultButtonColor,
});
return Settings.updateOne({ _id }, { $set: { value: newDefaultButtonColor } });
})
.filter(isTruthy);
const buttonLabelColorPromises = customOAuthButtonLabelColors
.map(({ _id, value, packageValue }) => {
if (packageValue !== value) {
return;
}
SystemLogger.warn({
msg: 'The default value of the custom setting has changed. Please review your settings.',
_id,
newValue: newDefaultButtonLabelColor,
});
return Settings.updateOne({ _id }, { $set: { value: newDefaultButtonLabelColor } });
})
.filter(isTruthy);
await Promise.all([...buttonColorPromises, ...buttonLabelColorPromises]);
// update login service configurations
await updateOAuthServices();
},
});

View File

@@ -0,0 +1,11 @@
import { Settings } from '@rocket.chat/models';
import { addMigration } from '../../lib/migrations';
addMigration({
version: 318,
name: 'Remove "E2E Encryption" setting group',
async up() {
await Settings.deleteOne({ _id: 'E2E Encryption', type: 'group' });
},
});

View File

@@ -0,0 +1,15 @@
import { LivechatRooms } from '@rocket.chat/models';
import { addMigration } from '../../lib/migrations';
addMigration({
version: 319,
name: 'Remove "pdfTranscriptRequested" index from LivechatRooms collection',
async up() {
try {
await LivechatRooms.col.dropIndex('pdfTranscriptRequested_1');
} catch {
console.error('Error dropping index');
}
},
});

View File

@@ -0,0 +1,10 @@
import { Settings } from '@rocket.chat/models';
import { addMigration } from '../../lib/migrations';
addMigration({
version: 320,
async up() {
await Settings.deleteOne({ _id: 'API_Use_REST_For_DDP_Calls' });
},
});

View File

@@ -0,0 +1,11 @@
import { Settings } from '@rocket.chat/models';
import { addMigration } from '../../lib/migrations';
addMigration({
version: 321,
name: 'Remove voxtelesys & mobex related settings',
async up() {
await Promise.all([Settings.deleteMany({ section: { $in: ['Voxtelesys', 'Mobex'] } })]);
},
});

View File

@@ -0,0 +1,21 @@
import { Settings } from '@rocket.chat/models';
import { addMigration } from '../../lib/migrations';
addMigration({
version: 322,
name: 'Remove Tokenpass settings',
async up() {
await Settings.deleteMany({
_id: {
$in: [
'Accounts_OAuth_Tokenpass',
'API_Tokenpass_URL',
'Accounts_OAuth_Tokenpass_id',
'Accounts_OAuth_Tokenpass_secret',
'Accounts_OAuth_Tokenpass_callback_url',
],
},
});
},
});

View File

@@ -0,0 +1,27 @@
import { AppLogs } from '@rocket.chat/models';
import { addMigration } from '../../lib/migrations';
// Remove outdated indexes on rocketchat_apps_logs
addMigration({
version: 323,
name: 'Remove outdated indexes on rocketchat_apps_logs',
async up() {
// Idempotent drops: ignore "IndexNotFound" (code 27)
try {
await AppLogs.col.dropIndex('appId_indexed_query');
} catch (e: any) {
if (e?.code !== 27 && e?.codeName !== 'IndexNotFound') {
throw e;
}
}
try {
await AppLogs.col.dropIndex('general_logs_index');
} catch (e: any) {
if (e?.code !== 27 && e?.codeName !== 'IndexNotFound') {
throw e;
}
}
},
});

View File

@@ -0,0 +1,11 @@
import { Settings } from '@rocket.chat/models';
import { addMigration } from '../../lib/migrations';
addMigration({
version: 324,
name: 'Remove Log_View_Limit setting',
async up() {
await Settings.deleteOne({ _id: 'Log_View_Limit' });
},
});

View File

@@ -0,0 +1,43 @@
import { Settings, Permissions } from '@rocket.chat/models';
import { addMigration } from '../../lib/migrations';
addMigration({
version: 325,
name: 'Remove VoIP_Omnichannel Settings and Permissions',
async up() {
await Settings.deleteMany({
_id: {
$in: [
'VoIP_Omnichannel',
'VoIP_Enabled',
'VoIP_JWT_Secret',
'Voip_Server_Configuration',
'VoIP_Server_Name',
'VoIP_Server_Websocket_Path',
'VoIP_Retry_Count',
'VoIP_Enable_Keep_Alive_For_Unstable_Networks',
'VoIP_Management_Server_Host',
'VoIP_Management_Server_Port',
'VoIP_Management_Server_Name',
'VoIP_Management_Server_Username',
'VoIP_Management_Server_Password',
],
},
});
await Permissions.deleteMany({
_id: {
$in: [
'manage-voip-call-settings',
'manage-voip-contact-center-settings',
'manage-agent-extension-association',
'inbound-voip-calls',
'spy-voip-calls',
'outbound-voip-calls',
'view-agent-extension-association',
],
},
});
},
});

View File

@@ -0,0 +1,23 @@
import { Rooms } from '@rocket.chat/models';
import { addMigration } from '../../lib/migrations';
addMigration({
version: 326,
name: 'Close Omnichannel VoIP Rooms',
async up() {
await Rooms.updateMany(
{
t: 'v' as any,
open: {
$exists: true,
},
},
{
$unset: {
open: 1,
},
},
);
},
});

View File

@@ -0,0 +1,22 @@
import { Settings } from '@rocket.chat/models';
import { addMigration } from '../../lib/migrations';
addMigration({
version: 327,
name: 'Remove FreeSwitch Settings',
async up() {
await Settings.deleteMany({
_id: {
$in: [
'VoIP_TeamCollab_FreeSwitch',
'VoIP_TeamCollab_FreeSwitch_Host',
'VoIP_TeamCollab_FreeSwitch_Port',
'VoIP_TeamCollab_FreeSwitch_Password',
'VoIP_TeamCollab_FreeSwitch_Timeout',
'VoIP_TeamCollab_FreeSwitch_WebSocket_Path',
],
},
});
},
});

View File

@@ -0,0 +1,15 @@
import { Permissions } from '@rocket.chat/models';
import { addMigration } from '../../lib/migrations';
addMigration({
version: 328,
name: 'Remove Old Voip Permissions',
async up() {
await Permissions.deleteMany({
_id: {
$in: ['manage-voip-extensions', 'view-user-voip-extension', 'view-voip-extension-details'],
},
});
},
});

View File

@@ -0,0 +1,11 @@
import { Settings } from '@rocket.chat/models';
import { addMigration } from '../../lib/migrations';
addMigration({
version: 329,
name: 'Remove VoIP Toggle Setting',
async up() {
await Settings.deleteOne({ _id: 'VoIP_TeamCollab_Enabled' });
},
});

View File

@@ -0,0 +1,11 @@
import { Settings } from '@rocket.chat/models';
import { addMigration } from '../../lib/migrations';
addMigration({
version: 330,
name: 'Remove ECDH setting',
async up() {
await Settings.deleteOne({ _id: 'ECDH_Enabled' });
},
});

View File

@@ -0,0 +1,11 @@
import { Permissions } from '@rocket.chat/models';
import { addMigration } from '../../lib/migrations';
addMigration({
version: 331,
name: 'Remove view-livechat-current-chats permission',
async up() {
await Permissions.deleteOne({ _id: 'view-livechat-current-chats' });
},
});

View File

@@ -0,0 +1,53 @@
import { CallHistory, Users } from '@rocket.chat/models';
import { addMigration } from '../../lib/migrations';
addMigration({
version: 332,
name: 'Fill contact information on older call history entries',
async up() {
const cursor = CallHistory.col.aggregate([
{
$match: {
external: false,
contactId: { $exists: true },
contactName: { $exists: false },
contactUsername: { $exists: false },
},
},
{
$lookup: {
from: Users.col.collectionName,
localField: 'contactId',
foreignField: '_id',
as: 'contactDetails',
},
},
{
$addFields: {
contactName: { $first: '$contactDetails.name' },
contactUsername: { $first: '$contactDetails.username' },
},
},
{
$project: {
contactName: 1,
contactUsername: 1,
},
},
{
$merge: {
into: CallHistory.col.collectionName,
on: '_id',
whenMatched: 'merge',
},
},
]);
// eslint-disable-next-line @typescript-eslint/no-unused-vars
for await (const _item of cursor) {
//
}
},
});

View File

@@ -0,0 +1,35 @@
import { Settings } from '@rocket.chat/models';
import { addMigration } from '../../lib/migrations';
addMigration({
version: 333,
name: 'Remove Voxtelesys & Mobex SMS settings by ID',
async up() {
const ids = [
// Voxtelesys settings
'SMS_Voxtelesys_authToken',
'SMS_Voxtelesys_URL',
'SMS_Voxtelesys_FileUpload_Enabled',
'SMS_Voxtelesys_FileUpload_MediaTypeWhiteList',
// Mobex settings
'SMS_Mobex_gateway_address',
'SMS_Mobex_gateway_address_desc',
'SMS_Mobex_from_number',
'SMS_Mobex_from_number_desc',
'SMS_Mobex_from_numbers_list',
'SMS_Mobex_from_numbers_list_desc',
'SMS_Mobex_password',
'SMS_Mobex_restful_address',
'SMS_Mobex_restful_address_desc',
'SMS_Mobex_username',
];
await Settings.deleteMany({
_id: { $in: ids },
});
await Settings.findOneAndUpdate({ _id: 'SMS_Service', value: { $ne: 'twilio' } }, { $set: { value: 'twilio' } });
},
});

View File

@@ -0,0 +1,22 @@
import { Settings } from '@rocket.chat/models';
import { addMigration } from '../../lib/migrations';
addMigration({
version: 334,
name: 'Remove webRTC settings',
async up() {
await Settings.deleteMany({
_id: {
$in: [
'WebRTC_Enabled',
'WebRTC_Enable_Channel',
'WebRTC_Enable_Private',
'WebRTC_Enable_Direct',
'WebRTC_Calls_Count',
'WebRTC_Servers',
],
},
});
},
});

View File

@@ -0,0 +1,11 @@
import { Settings } from '@rocket.chat/models';
import { addMigration } from '../../lib/migrations';
addMigration({
version: 335,
name: 'Remove OTR_Enable and OTR_Count settings',
async up() {
await Settings.deleteMany({ _id: { $in: ['OTR_Enable', 'OTR_Count'] } });
},
});

View File

@@ -0,0 +1,102 @@
import { Permissions, Roles, Settings, Users } from '@rocket.chat/models';
import type { UpdateResult } from 'mongodb';
import { upsertPermissions } from '../../lib/authorization/upsertPermissions';
import { migrateDatabase, onServerVersionChange } from '../../lib/migrations';
import { settings } from '../../settings';
import { ensureCloudWorkspaceRegistered } from '../cloudRegistration';
const { MIGRATION_VERSION = 'latest' } = process.env;
const [version, ...subcommands] = MIGRATION_VERSION.split(',');
const maxAgeSettingMap = new Map([
['RetentionPolicy_MaxAge_Channels', 'RetentionPolicy_TTL_Channels'],
['RetentionPolicy_MaxAge_Groups', 'RetentionPolicy_TTL_Groups'],
['RetentionPolicy_MaxAge_DMs', 'RetentionPolicy_TTL_DMs'],
]);
const moveRetentionSetting = async () => {
const convertDaysToMs = (days: number) => days * 24 * 60 * 60 * 1000;
const promises: Array<Promise<UpdateResult>> = [];
await Settings.find(
{ _id: { $in: Array.from(maxAgeSettingMap.keys()) }, value: { $ne: -1 } },
{ projection: { _id: 1, value: 1 } },
).forEach(({ _id, value }) => {
const newSettingId = maxAgeSettingMap.get(_id);
if (!newSettingId) {
throw new Error(`moveRetentionSetting - Setting ${_id} equivalent does not exist`);
}
const newValue = convertDaysToMs(Number(value));
promises.push(
Settings.updateOne(
{
_id: maxAgeSettingMap.get(_id),
},
{
$set: {
value: newValue,
},
},
),
);
const currentCache = settings.getSetting(newSettingId);
if (!currentCache) {
return;
}
settings.set({ ...currentCache, value: newValue });
});
await Promise.all(promises);
await Settings.updateMany({ _id: { $in: Array.from(maxAgeSettingMap.keys()) } }, { $set: { value: -1 } });
};
async function setPermissionsToNewRole() {
const role = await Roles.findOneById('federated-external');
// if role was found it means it was already created with the permissions, so we can skip it
if (role) {
return;
}
// if federation was used before the new role was created, we need to update their roles with the new role
await Users.updateMany({ federated: true }, { $addToSet: { roles: 'federated-external' } });
const rolePermissions = [
'create-c',
'create-d',
'create-p',
'delete-own-message',
'leave-c',
'leave-p',
'mention-all',
'mention-here',
'start-discussion',
'start-discussion-other-user',
'view-c-room',
'view-d-room',
'view-p-room',
'preview-c-room',
'view-outside-room',
'mobile-upload-file',
'access-federation',
];
// since this is a one time query, no need to create a method in model class for it
await Permissions.updateMany({ _id: { $in: rolePermissions } }, { $addToSet: { roles: 'federated-external' } });
}
export const performMigrationProcedure = async (): Promise<void> => {
await migrateDatabase(version === 'latest' ? version : parseInt(version), subcommands);
// perform operations when the server is starting with a different version
await onServerVersionChange(async () => {
await setPermissionsToNewRole();
await upsertPermissions();
await ensureCloudWorkspaceRegistered();
await moveRetentionSetting();
});
};