From f29e839be45d361e9fc9cf58a04f8d53b457c44a Mon Sep 17 00:00:00 2001 From: Ricardo Garim Date: Fri, 24 Oct 2025 08:02:26 -0300 Subject: [PATCH 1/4] add watcher for federation settings and update config service when it happens --- apps/meteor/ee/server/startup/federation.ts | 44 ++++++++++++++------ ee/packages/federation-matrix/src/setup.ts | 46 +++++++++++++-------- 2 files changed, 60 insertions(+), 30 deletions(-) diff --git a/apps/meteor/ee/server/startup/federation.ts b/apps/meteor/ee/server/startup/federation.ts index d2e20f9ead1e7..f3615eadd2a42 100644 --- a/apps/meteor/ee/server/startup/federation.ts +++ b/apps/meteor/ee/server/startup/federation.ts @@ -3,33 +3,53 @@ import { FederationMatrix, setupFederationMatrix } from '@rocket.chat/federation import { InstanceStatus } from '@rocket.chat/instance-status'; import { Logger } from '@rocket.chat/logger'; +import { settings } from '../../../app/settings/server'; import { StreamerCentral } from '../../../server/modules/streamer/streamer.module'; import { registerFederationRoutes } from '../api/federation'; const logger = new Logger('Federation'); +const CRITICAL_SETTINGS = [ + 'Federation_Service_Enabled', + 'Federation_Service_Domain', + 'Federation_Service_Matrix_Signing_Key', + 'Federation_Service_Matrix_Signing_Algorithm', + 'Federation_Service_Matrix_Signing_Version', + 'Federation_Service_Join_Encrypted_Rooms', + 'Federation_Service_Join_Non_Private_Rooms', + 'Federation_Service_EDU_Process_Typing', + 'Federation_Service_EDU_Process_Presence', +]; + +let serviceRegistered = false; + export const startFederationService = async (): Promise => { try { const isEnabled = await setupFederationMatrix(InstanceStatus.id()); - api.registerService(new FederationMatrix()); + if (!serviceRegistered) { + api.registerService(new FederationMatrix()); + serviceRegistered = true; + } await registerFederationRoutes(); - // only registers the typing listener if the service is enabled - if (!isEnabled) { - return; + if (isEnabled) { + // TODO move to service/setup? + StreamerCentral.on('broadcast', (name, eventName, args) => { + if (name === 'notify-room' && eventName.endsWith('user-activity')) { + const [rid] = eventName.split('/'); + const [user, activity] = args; + void FederationMatrixService.notifyUserTyping(rid, user, activity.includes('user-typing')); + } + }); } - // TODO move to service/setup? - StreamerCentral.on('broadcast', (name, eventName, args) => { - if (name === 'notify-room' && eventName.endsWith('user-activity')) { - const [rid] = eventName.split('/'); - const [user, activity] = args; - void FederationMatrixService.notifyUserTyping(rid, user, activity.includes('user-typing')); - } + settings.watchMultiple(CRITICAL_SETTINGS, async () => { + await startFederationService(); }); } catch (error) { - logger.error('Failed to start federation-matrix service:', error); + logger.error('Failed to start federation service', { error }); + throw error; } }; diff --git a/ee/packages/federation-matrix/src/setup.ts b/ee/packages/federation-matrix/src/setup.ts index 8f82e3062272c..0ee13f9eec63a 100644 --- a/ee/packages/federation-matrix/src/setup.ts +++ b/ee/packages/federation-matrix/src/setup.ts @@ -1,7 +1,7 @@ import { License } from '@rocket.chat/core-services'; import { Emitter } from '@rocket.chat/emitter'; import type { HomeserverEventSignatures } from '@rocket.chat/federation-sdk'; -import { ConfigService, createFederationContainer } from '@rocket.chat/federation-sdk'; +import { ConfigService, createFederationContainer, getAllServices } from '@rocket.chat/federation-sdk'; import { Logger } from '@rocket.chat/logger'; import { Settings } from '@rocket.chat/models'; @@ -9,6 +9,8 @@ import { registerEvents } from './events'; const logger = new Logger('FederationSetup'); +let containerInitialized = false; + function validateDomain(domain: string): boolean { const value = domain.trim(); @@ -52,7 +54,7 @@ export async function setupFederationMatrix(instanceId: string): Promise(); + if (!containerInitialized) { + const config = new ConfigService(configValues); + const eventHandler = new Emitter(); - await createFederationContainer( - { - emitter: eventHandler, - }, - config, - ); + await createFederationContainer( + { + emitter: eventHandler, + }, + config, + ); - const serviceEnabled = (await License.hasModule('federation')) && settingEnabled && validateDomain(serverName); - if (!serviceEnabled) { - return false; - } + const serviceEnabled = (await License.hasModule('federation')) && settingEnabled && validateDomain(serverName); + if (!serviceEnabled) { + return false; + } - registerEvents(eventHandler, serverName, { - typing: processEDUTyping, - presence: processEDUPresence, - }); + registerEvents(eventHandler, serverName, { + typing: processEDUTyping, + presence: processEDUPresence, + }); + + containerInitialized = true; + } else { + const services = getAllServices(); + services.config.updateConfig(configValues); + } return true; } From b48a1ae0dc82c7ab30d7e9ba89c16ec0c5656d9b Mon Sep 17 00:00:00 2001 From: Ricardo Garim Date: Fri, 24 Oct 2025 08:42:28 -0300 Subject: [PATCH 2/4] fix service init --- apps/meteor/ee/server/startup/federation.ts | 23 ++++++++++++--------- ee/packages/federation-matrix/src/setup.ts | 10 ++++----- 2 files changed, 18 insertions(+), 15 deletions(-) diff --git a/apps/meteor/ee/server/startup/federation.ts b/apps/meteor/ee/server/startup/federation.ts index f3615eadd2a42..aede82d0bb93f 100644 --- a/apps/meteor/ee/server/startup/federation.ts +++ b/apps/meteor/ee/server/startup/federation.ts @@ -22,19 +22,19 @@ const CRITICAL_SETTINGS = [ ]; let serviceRegistered = false; +let watcherRegistered = false; export const startFederationService = async (): Promise => { try { const isEnabled = await setupFederationMatrix(InstanceStatus.id()); + if (isEnabled) { + if (!serviceRegistered) { + api.registerService(new FederationMatrix()); + serviceRegistered = true; + } - if (!serviceRegistered) { - api.registerService(new FederationMatrix()); - serviceRegistered = true; - } - - await registerFederationRoutes(); + await registerFederationRoutes(); - if (isEnabled) { // TODO move to service/setup? StreamerCentral.on('broadcast', (name, eventName, args) => { if (name === 'notify-room' && eventName.endsWith('user-activity')) { @@ -45,9 +45,12 @@ export const startFederationService = async (): Promise => { }); } - settings.watchMultiple(CRITICAL_SETTINGS, async () => { - await startFederationService(); - }); + if (!watcherRegistered) { + settings.watchMultiple(CRITICAL_SETTINGS, async () => { + await startFederationService(); + }); + watcherRegistered = true; + } } catch (error) { logger.error('Failed to start federation service', { error }); throw error; diff --git a/ee/packages/federation-matrix/src/setup.ts b/ee/packages/federation-matrix/src/setup.ts index 0ee13f9eec63a..55010b600edbe 100644 --- a/ee/packages/federation-matrix/src/setup.ts +++ b/ee/packages/federation-matrix/src/setup.ts @@ -42,6 +42,11 @@ export async function setupFederationMatrix(instanceId: string): Promise('Federation_Service_Enabled')) || false; const serverName = (await Settings.getValueById('Federation_Service_Domain')) || ''; + const serviceEnabled = (await License.hasModule('federation')) && settingEnabled && validateDomain(serverName); + if (!serviceEnabled) { + return false; + } + const processEDUTyping = (await Settings.getValueById('Federation_Service_EDU_Process_Typing')) || false; const processEDUPresence = (await Settings.getValueById('Federation_Service_EDU_Process_Presence')) || false; const signingKey = (await Settings.getValueById('Federation_Service_Matrix_Signing_Key')) || ''; @@ -104,11 +109,6 @@ export async function setupFederationMatrix(instanceId: string): Promise Date: Mon, 27 Oct 2025 08:16:21 -0300 Subject: [PATCH 3/4] fix: register federation routes only once --- apps/meteor/ee/server/startup/federation.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/apps/meteor/ee/server/startup/federation.ts b/apps/meteor/ee/server/startup/federation.ts index aede82d0bb93f..533dddf504fb1 100644 --- a/apps/meteor/ee/server/startup/federation.ts +++ b/apps/meteor/ee/server/startup/federation.ts @@ -30,11 +30,10 @@ export const startFederationService = async (): Promise => { if (isEnabled) { if (!serviceRegistered) { api.registerService(new FederationMatrix()); + await registerFederationRoutes(); serviceRegistered = true; } - await registerFederationRoutes(); - // TODO move to service/setup? StreamerCentral.on('broadcast', (name, eventName, args) => { if (name === 'notify-room' && eventName.endsWith('user-activity')) { From 616c805cd9975a95f9fd8a3705eb9499934ec950 Mon Sep 17 00:00:00 2001 From: Ricardo Garim Date: Mon, 27 Oct 2025 08:20:03 -0300 Subject: [PATCH 4/4] fix: prevent duplicate StreamerCentral listener registration --- apps/meteor/ee/server/startup/federation.ts | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/apps/meteor/ee/server/startup/federation.ts b/apps/meteor/ee/server/startup/federation.ts index 533dddf504fb1..1c6889c018b24 100644 --- a/apps/meteor/ee/server/startup/federation.ts +++ b/apps/meteor/ee/server/startup/federation.ts @@ -30,18 +30,20 @@ export const startFederationService = async (): Promise => { if (isEnabled) { if (!serviceRegistered) { api.registerService(new FederationMatrix()); + await registerFederationRoutes(); + + // TODO move to service/setup? + StreamerCentral.on('broadcast', (name, eventName, args) => { + if (name === 'notify-room' && eventName.endsWith('user-activity')) { + const [rid] = eventName.split('/'); + const [user, activity] = args; + void FederationMatrixService.notifyUserTyping(rid, user, activity.includes('user-typing')); + } + }); + serviceRegistered = true; } - - // TODO move to service/setup? - StreamerCentral.on('broadcast', (name, eventName, args) => { - if (name === 'notify-room' && eventName.endsWith('user-activity')) { - const [rid] = eventName.split('/'); - const [user, activity] = args; - void FederationMatrixService.notifyUserTyping(rid, user, activity.includes('user-typing')); - } - }); } if (!watcherRegistered) {