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,44 @@
import { Users } from '@rocket.chat/models';
import { Accounts } from 'meteor/accounts-base';
import _ from 'underscore';
export async function configureAccounts() {
const orig_updateOrCreateUserFromExternalService = Accounts.updateOrCreateUserFromExternalService;
Accounts.updateOrCreateUserFromExternalService = async function (serviceName, serviceData = {}, ...args /* , options*/) {
const services = ['facebook', 'github', 'gitlab', 'google', 'meteor-developer', 'linkedin', 'twitter', 'apple'];
if (services.includes(serviceName) === false && serviceData._OAuthCustom !== true) {
return orig_updateOrCreateUserFromExternalService.apply(this, [serviceName, serviceData, ...args]);
}
if (serviceName === 'meteor-developer') {
if (Array.isArray(serviceData.emails)) {
const primaryEmail = serviceData.emails.sort((a) => a.primary !== true).filter((item) => item.verified === true)[0];
serviceData.email = primaryEmail && primaryEmail.address;
}
}
if (serviceData.email) {
const user = await Users.findOneByEmailAddress(serviceData.email);
if (user != null && user.services?.[serviceName]?.id !== serviceData.id) {
const findQuery = {
address: serviceData.email,
verified: true,
};
if (user.services?.password && !_.findWhere(user.emails, findQuery)) {
await Users.resetPasswordAndSetRequirePasswordChange(
user._id,
true,
'This_email_has_already_been_used_and_has_not_been_verified__Please_change_your_password',
);
}
await Users.setServiceId(user._id, serviceName, serviceData.id);
await Users.setEmailVerified(user._id, serviceData.email);
}
}
return orig_updateOrCreateUserFromExternalService.apply(this, [serviceName, serviceData, ...args]);
};
}

View File

@@ -0,0 +1,37 @@
import debounce from 'lodash.debounce';
import { Accounts } from 'meteor/accounts-base';
import { RoutePolicy } from 'meteor/routepolicy';
import { WebApp } from 'meteor/webapp';
import { loginHandlerCAS } from '../lib/cas/loginHandler';
import { middlewareCAS } from '../lib/cas/middleware';
import { updateCasServices } from '../lib/cas/updateCasService';
import type { ICachedSettings } from '../settings/CachedSettings';
export async function configureCAS(settings: ICachedSettings) {
const _updateCasServices = debounce(updateCasServices, 2000);
settings.watchByRegex(/^CAS_.+/, async () => {
await _updateCasServices();
});
RoutePolicy.declare('/_cas/', 'network');
// Listen to incoming OAuth http requests
WebApp.connectHandlers.use((req, res, next) => {
middlewareCAS(req, res, next);
});
/*
* Register a server-side login handler.
* It is called after Accounts.callLoginMethod() is called from client.
*
*/
Accounts.registerLoginHandler('cas', (options) => {
const promise = loginHandlerCAS(options);
// Pretend the promise has been awaited so the types will match -
// #TODO: Fix registerLoginHandler's type definitions (it accepts promises)
return promise as unknown as Awaited<typeof promise>;
});
}

View File

@@ -0,0 +1,6 @@
import { RocketChatAssets } from '../lib/media/assets';
import type { ICachedSettings } from '../settings/CachedSettings';
export async function configureAssets(settings: ICachedSettings): Promise<void> {
settings.watchByRegex(/^Assets_/, (key, value) => RocketChatAssets.processAsset(key, value));
}

View File

@@ -0,0 +1,61 @@
import { createHash } from 'node:crypto';
import { Meteor } from 'meteor/meteor';
import { WebApp, WebAppInternals } from 'meteor/webapp';
import type { ICachedSettings } from '../settings/CachedSettings';
const webAppHashes: Record<string, string> = {};
export function getWebAppHash(arch: string): string | undefined {
if (!webAppHashes[arch]) {
const program = WebApp.clientPrograms[arch] as (typeof WebApp.clientPrograms)[string] & {
meteorRuntimeConfig: string;
};
webAppHashes[arch] = createHash('sha1')
.update(JSON.stringify(encodeURIComponent(program.meteorRuntimeConfig)))
.digest('hex');
}
return webAppHashes[arch];
}
const { generateBoilerplate } = WebAppInternals;
WebAppInternals.generateBoilerplate = function (...args: Parameters<typeof generateBoilerplate>) {
for (const arch of Object.keys(WebApp.clientPrograms)) {
delete webAppHashes[arch];
}
return generateBoilerplate.apply(this, args);
};
export function configureBoilerplate(settings: ICachedSettings): void {
settings.watch<string>(
'Site_Url',
// Needed as WebAppInternals.generateBoilerplate needs to be called in a fiber
Meteor.bindEnvironment(async (value) => {
if (value == null || value.trim() === '') {
return;
}
let host = value.replace(/\/$/, '');
// let prefix = '';
const match = value.match(/([^\/]+\/{2}[^\/]+)(\/.+)/);
if (match != null) {
host = match[1];
// prefix = match[2].replace(/\/$/, '');
}
(global as any).__meteor_runtime_config__.ROOT_URL = value;
if (Meteor.absoluteUrl.defaultOptions?.rootUrl) {
Meteor.absoluteUrl.defaultOptions.rootUrl = value;
}
// hostname = host.replace(/^https?:\/\//, '');
process.env.MOBILE_ROOT_URL = host;
process.env.MOBILE_DDP_URL = host;
if (typeof WebAppInternals !== 'undefined' && WebAppInternals.generateBoilerplate) {
await WebAppInternals.generateBoilerplate();
}
}),
);
}

View File

@@ -0,0 +1,32 @@
import { WebAppInternals } from 'meteor/webapp';
import type { ICachedSettings } from '../settings/CachedSettings';
export async function configureCDN(settings: ICachedSettings): Promise<void> {
settings.change<string>('CDN_PREFIX', (value) => {
const useForAll = settings.get('CDN_PREFIX_ALL');
if (value && useForAll) {
WebAppInternals.setBundledJsCssPrefix(value);
}
});
settings.change<string>('CDN_JSCSS_PREFIX', (value) => {
const useForAll = settings.get('CDN_PREFIX_ALL');
if (value && typeof value === 'string' && value.trim() && !useForAll) {
WebAppInternals.setBundledJsCssPrefix(value);
}
});
const cdnValue = settings.get('CDN_PREFIX');
const useForAll = settings.get('CDN_PREFIX_ALL');
const cdnJsCss = settings.get('CDN_JSCSS_PREFIX');
if (cdnValue && typeof cdnValue === 'string' && cdnValue.trim()) {
if (useForAll) {
WebAppInternals.setBundledJsCssPrefix(cdnValue);
return;
}
if (cdnJsCss && typeof cdnJsCss === 'string' && cdnJsCss.trim()) {
WebAppInternals.setBundledJsCssPrefix(cdnJsCss);
}
}
}

View File

@@ -0,0 +1,11 @@
import { setCachingVersion, setInlineScriptsAllowed } from '../lib/cors/cors';
import type { ICachedSettings } from '../settings/CachedSettings';
export async function configureCORS(settings: ICachedSettings): Promise<void> {
settings.watch<boolean>('Enable_CSP', async (enabled) => {
await setInlineScriptsAllowed(!enabled);
});
settings.watch<string>('Troubleshoot_Force_Caching_Version', (value) => {
setCachingVersion(value);
});
}

View File

@@ -0,0 +1,45 @@
import _ from 'underscore';
import { logger } from '../features/EmailInbox/logger';
import { DirectReplyIMAPInterceptor, POP3Helper } from '../lib/notifications/interceptDirectReplyEmails';
import type { ICachedSettings } from '../settings/CachedSettings';
export async function configureDirectReply(settings: ICachedSettings): Promise<void> {
let client: DirectReplyIMAPInterceptor | POP3Helper | undefined;
const startEmailInterceptor = _.debounce(async () => {
logger.info('Email Interceptor...');
const protocol = settings.get('Direct_Reply_Protocol');
const isEnabled =
settings.get('Direct_Reply_Enable') &&
protocol &&
settings.get('Direct_Reply_Host') &&
settings.get('Direct_Reply_Port') &&
settings.get('Direct_Reply_Username') &&
settings.get('Direct_Reply_Password');
if (client) {
await client.stop();
}
if (!isEnabled) {
logger.info('Email Interceptor Stopped...');
return;
}
logger.info('Starting Email Interceptor...');
if (protocol === 'IMAP') {
client = new DirectReplyIMAPInterceptor();
await client.start();
}
if (protocol === 'POP') {
client = new POP3Helper(settings.get('Direct_Reply_Frequency'));
client.start();
}
}, 1000);
settings.watchByRegex(/^Direct_Reply_.+/, startEmailInterceptor);
void startEmailInterceptor();
}

View File

@@ -0,0 +1,28 @@
import { Meteor } from 'meteor/meteor';
import Bridge from '../bridges/irc/irc-bridge';
import type { ICachedSettings } from '../settings/CachedSettings';
export async function configureIRC(settings: ICachedSettings): Promise<void> {
if (!settings.get('IRC_Enabled')) {
return;
}
// Normalize the config values
const config = {
server: {
protocol: settings.get('IRC_Protocol'),
host: settings.get('IRC_Host'),
port: settings.get('IRC_Port'),
name: settings.get('IRC_Name'),
description: settings.get('IRC_Description'),
},
passwords: {
local: settings.get('IRC_Local_Password'),
peer: settings.get('IRC_Peer_Password'),
},
};
Meteor.ircBridge = new Bridge(config);
await Meteor.ircBridge.init();
}

View File

@@ -0,0 +1,17 @@
import { logLevel, type LogLevelSetting } from '@rocket.chat/logger';
import { Settings } from '@rocket.chat/models';
import type { ICachedSettings } from '../settings/CachedSettings';
export async function configureLogLevel(settings: ICachedSettings) {
const LogLevel = await Settings.getValueById('Log_Level');
if (LogLevel) {
logLevel.emit('changed', LogLevel as LogLevelSetting);
}
settings.watch('Log_Level', (value) => {
if (value != null) {
logLevel.emit('changed', String(value) as LogLevelSetting);
}
});
}

View File

@@ -0,0 +1,104 @@
import { Users } from '@rocket.chat/models';
import bodyParser from 'body-parser';
import MongoStore from 'connect-mongo';
import express from 'express';
import rateLimit from 'express-rate-limit';
import session from 'express-session';
import { MongoInternals } from 'meteor/mongo';
import { WebApp } from 'meteor/webapp';
import passport from 'passport';
import { configureOAuthServices } from '../lib/oauth/configureOAuthServices';
import { createOAuthServiceConfig } from '../lib/oauth/createOAuthServiceConfig';
import { getOAuthServices } from '../lib/oauth/getOAuthServices';
import type { ICachedSettings } from '../settings/CachedSettings';
const oAuthPaths = ['/oauth', '/_oauth'];
const { Router: router } = express;
export const oAuthRouter = router();
const oAuthApp = express();
oAuthApp.set('trust proxy', 1);
export const configurePassport = (settings: ICachedSettings) => {
const { client } = MongoInternals.defaultRemoteCollectionDriver().mongo;
oAuthApp.use(
oAuthPaths,
session({
name: 'oauth',
secret: settings.get<string>('Accounts_OAuth_Session_Secret'),
resave: false,
saveUninitialized: false,
proxy: true,
store: MongoStore.create({
client,
collectionName: 'rocketchat_oauth_sessions',
ttl: 5 * 60,
autoRemove: 'native',
}),
cookie: {
httpOnly: true,
secure: process.env.NODE_ENV === 'production',
maxAge: 5 * 60 * 1000, // 5 minutes
sameSite: process.env.NODE_ENV === 'production' ? 'none' : 'lax',
},
}),
);
oAuthApp.use(oAuthPaths, passport.initialize());
oAuthApp.use(oAuthPaths, passport.session());
oAuthApp.use(oAuthPaths, bodyParser.urlencoded({ extended: true }));
const createOAuthRateLimiter = () =>
rateLimit({
windowMs: settings.get<number>('API_Enable_Rate_Limiter_Limit_Time_Default'),
max: settings.get<number>('API_Enable_Rate_Limiter_Limit_Calls_Default'),
skip: () =>
process.env.TEST_MODE === 'true' ||
process.env.TEST_MODE === 'api' ||
settings.get<boolean>('API_Enable_Rate_Limiter') !== true ||
(process.env.NODE_ENV === 'development' && settings.get<boolean>('API_Enable_Rate_Limiter_Dev') !== true),
handler: (_req, res) => {
res.status(429).json({
success: false,
error: 'Too many requests. Please try again later.',
});
},
});
let oauthRateLimiter = createOAuthRateLimiter();
settings.watchMultiple(['API_Enable_Rate_Limiter_Limit_Time_Default', 'API_Enable_Rate_Limiter_Limit_Calls_Default'], () => {
oauthRateLimiter = createOAuthRateLimiter();
});
oAuthRouter.use(oAuthPaths, (req, res, next) => oauthRateLimiter(req, res, next));
// Register OAuth Routes
oAuthApp.use(oAuthRouter);
passport.serializeUser((user: any, done) => {
done(null, user._id);
});
passport.deserializeUser(async (id, done) => {
const user = await Users.findOneById(id as string, { projection: { __rooms: 0 } });
// we don’t actually use this user later
done(null, user);
});
settings.watchByRegex(/^(Accounts_OAuth_[a-z0-9_]+|API_GitHub_Enterprise_URL)$/i, () => {
if (!settings.get<boolean>('Accounts_OAuth_Use_Modern_Flow')) {
return;
}
const services = getOAuthServices(settings);
const oauthServiceConfigs = createOAuthServiceConfig(settings, services);
configureOAuthServices(oauthServiceConfigs, settings);
});
WebApp.rawConnectHandlers.use(oAuthApp);
};

View File

@@ -0,0 +1,34 @@
import { SystemLogger } from '../lib/logger/system';
import type { ICachedSettings } from '../settings/CachedSettings';
export function configureSMTP(settings: ICachedSettings): void {
settings.watchMultiple(
['SMTP_Host', 'SMTP_Port', 'SMTP_Username', 'SMTP_Password', 'SMTP_Protocol', 'SMTP_Pool', 'SMTP_IgnoreTLS'],
() => {
SystemLogger.info('Updating process.env.MAIL_URL');
if (!settings.get('SMTP_Host')) {
delete process.env.MAIL_URL;
return;
}
process.env.MAIL_URL = `${settings.get('SMTP_Protocol')}://`;
if (settings.get('SMTP_Username') && settings.get('SMTP_Password')) {
process.env.MAIL_URL += `${encodeURIComponent(settings.get('SMTP_Username'))}:${encodeURIComponent(settings.get('SMTP_Password'))}@`;
}
process.env.MAIL_URL += encodeURIComponent(settings.get('SMTP_Host'));
if (settings.get('SMTP_Port')) {
process.env.MAIL_URL += `:${parseInt(settings.get('SMTP_Port'))}`;
}
process.env.MAIL_URL += `?pool=${settings.get('SMTP_Pool')}`;
if (settings.get('SMTP_Protocol') === 'smtp' && settings.get('SMTP_IgnoreTLS')) {
process.env.MAIL_URL += '&secure=false&ignoreTLS=true';
}
},
);
}

View File

@@ -0,0 +1,34 @@
import { configureAccounts } from './accounts_meld';
import { configureCAS } from './cas';
import { configureAssets } from './configureAssets';
import { configureBoilerplate } from './configureBoilerplate';
import { configureCDN } from './configureCDN';
import { configureCORS } from './configureCORS';
import { configureDirectReply } from './configureDirectReply';
import { configureIRC } from './configureIRC';
import { configureLogLevel } from './configureLogLevel';
import { configurePassport } from './configurePassport';
import { configureSMTP } from './configureSMTP';
import { configureLDAP } from './ldap';
import { configureOAuth } from './oauth';
import { configurePushNotifications } from './pushNotification';
import type { ICachedSettings } from '../settings/CachedSettings';
export async function configureServer(settings: ICachedSettings) {
await Promise.all([
configureLogLevel(settings),
configureAccounts(),
configureCAS(settings),
configureLDAP(settings),
configureOAuth(settings),
configureAssets(settings),
configureCORS(settings),
configureCDN(settings),
configurePushNotifications(settings),
configureBoilerplate(settings),
configureDirectReply(settings),
configureSMTP(settings),
configureIRC(settings),
configurePassport(settings),
]);
}

View File

@@ -0,0 +1,55 @@
import { LDAP } from '@rocket.chat/core-services';
import { Accounts } from 'meteor/accounts-base';
import { callbacks } from '../lib/callbacks';
import { warnUnlicensedAuthService } from '../lib/premiumAuthDeprecation';
import type { ICachedSettings } from '../settings/CachedSettings';
export async function configureLDAP(settings: ICachedSettings): Promise<void> {
// Register ldap login handler
Accounts.registerLoginHandler('ldap', async (loginRequest: Record<string, any>) => {
if (!loginRequest.ldap || !loginRequest.ldapOptions) {
return undefined;
}
warnUnlicensedAuthService('LDAP', 'ldap-enterprise');
return LDAP.loginRequest(loginRequest.username, loginRequest.ldapPass);
});
// Prevent password logins by LDAP users when LDAP is enabled
let ldapEnabled: boolean;
settings.watch('LDAP_Enable', (value) => {
if (ldapEnabled === value) {
return;
}
ldapEnabled = value as boolean;
if (!value) {
return callbacks.remove('beforeValidateLogin', 'validateLdapLoginFallback');
}
callbacks.add(
'beforeValidateLogin',
(login: Record<string, any>) => {
if (!login.allowed) {
return login;
}
// The fallback setting should only block password logins, so users that have other login services can continue using them
if (login.type !== 'password') {
return login;
}
// LDAP users can still login locally when login fallback is enabled
if (login.user.services?.ldap?.id) {
login.allowed = settings.get<boolean>('LDAP_Login_Fallback') ?? false;
}
return login;
},
callbacks.priority.MEDIUM,
'validateLdapLoginFallback',
);
});
}

View File

@@ -0,0 +1,21 @@
import debounce from 'lodash.debounce';
import { initCustomOAuthServices } from '../lib/oauth/initCustomOAuthServices';
import { removeOAuthService } from '../lib/oauth/removeOAuthService';
import { updateOAuthServices } from '../lib/oauth/updateOAuthServices';
import type { ICachedSettings } from '../settings/CachedSettings';
export async function configureOAuth(settings: ICachedSettings): Promise<void> {
const _updateOAuthServices = debounce(updateOAuthServices, 2000);
settings.watchByRegex(/^Accounts_OAuth_.+/, () => {
return _updateOAuthServices();
});
settings.watchByRegex(/^Accounts_OAuth_Custom-[a-z0-9_]+/, (key, value) => {
if (!value) {
return removeOAuthService(key);
}
});
await initCustomOAuthServices();
}

View File

@@ -0,0 +1,73 @@
import { getWorkspaceAccessToken } from '../lib/cloud';
import { Push } from '../lib/notifications/push';
import type { ICachedSettings } from '../settings/CachedSettings';
export async function configurePushNotifications(settings: ICachedSettings): Promise<void> {
settings.watch<boolean>('Push_enable', async (enabled) => {
if (!enabled) {
await Push.unconfigure();
return;
}
const gateways =
settings.get('Push_enable_gateway') && settings.get('Register_Server') && settings.get('Cloud_Service_Agree_PrivacyTerms')
? settings.get<string>('Push_gateway').split('\n')
: undefined;
let apn:
| {
passphrase: string;
key: string;
cert: string;
gateway?: string;
}
| undefined;
let gcm:
| {
apiKey: string;
projectNumber: string;
}
| undefined;
// TODO: this part of the code should be refactored as the deprecated GCM methods are no longer being used and FCM is preferred.
if (!gateways) {
gcm = {
apiKey: 'TO_BE_REFACTORED',
projectNumber: 'TO_BE_REFACTORED',
};
apn = {
passphrase: settings.get('Push_apn_passphrase'),
key: settings.get('Push_apn_key'),
cert: settings.get('Push_apn_cert'),
};
if (settings.get('Push_production') !== true) {
apn = {
passphrase: settings.get('Push_apn_dev_passphrase'),
key: settings.get('Push_apn_dev_key'),
cert: settings.get('Push_apn_dev_cert'),
gateway: 'gateway.sandbox.push.apple.com',
};
}
if (!apn.key || apn.key.trim() === '' || !apn.cert || apn.cert.trim() === '') {
apn = undefined;
}
if (!gcm.apiKey || gcm.apiKey.trim() === '' || !gcm.projectNumber || gcm.projectNumber.trim() === '') {
gcm = undefined;
}
}
Push.configure({
apn,
gcm,
production: settings.get('Push_production'),
gateways,
uniqueId: settings.get('uniqueID'),
async getAuthorization() {
return `Bearer ${await getWorkspaceAccessToken()}`;
},
});
});
}