Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
78 changes: 78 additions & 0 deletions apps/meteor/ee/server/startup/federation.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
import { api } from '@rocket.chat/core-services';
import { FederationMatrix } from '@rocket.chat/federation-matrix';
import { License } from '@rocket.chat/license';
import { Logger } from '@rocket.chat/logger';

import { settings } from '../../../app/settings/server';
import { registerFederationRoutes } from '../api/federation';

const logger = new Logger('Federation');

export const startFederationService = async (): Promise<void> => {
let federationMatrixService: FederationMatrix | undefined;

const shouldStartService = (): boolean => {
const hasLicense = License.hasModule('federation');
const isEnabled = settings.get('Federation_Service_Enabled') === true;
return hasLicense && isEnabled;
};

const startService = async (): Promise<void> => {
if (federationMatrixService) {
logger.debug('Federation-matrix service already started... skipping');
return;
}

logger.debug('Starting federation-matrix service');
federationMatrixService = await FederationMatrix.create();

try {
api.registerService(federationMatrixService);
await registerFederationRoutes(federationMatrixService);
} catch (error) {
logger.error('Failed to start federation-matrix service:', error);
}
};

const stopService = async (): Promise<void> => {
if (!federationMatrixService) {
logger.debug('Federation-matrix service not registered... skipping');
return;
}

logger.debug('Stopping federation-matrix service');

// TODO: Unregister routes
// await unregisterFederationRoutes(federationMatrixService);

await api.destroyService(federationMatrixService);
federationMatrixService = undefined;
};

if (shouldStartService()) {
await startService();
}

void License.onLicense('federation', async () => {
logger.debug('Federation license became available');
if (shouldStartService()) {
await startService();
}
});

License.onInvalidateLicense(async () => {
logger.debug('License invalidated, checking federation module');
if (!shouldStartService()) {
await stopService();
}
});

settings.watch('Federation_Service_Enabled', async (enabled) => {
logger.debug('Federation_Service_Enabled setting changed:', enabled);
if (shouldStartService()) {
await startService();
} else {
await stopService();
}
});
};
7 changes: 0 additions & 7 deletions apps/meteor/server/services/startup.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,6 @@ import { UploadService } from './upload/service';
import { UserService } from './user/service';
import { VideoConfService } from './video-conference/service';
import { VoipAsteriskService } from './voip-asterisk/service';
import { registerFederationRoutes } from '../../ee/server/api/federation';
import { i18n } from '../lib/i18n';

export const registerServices = async (): Promise<void> => {
Expand Down Expand Up @@ -72,12 +71,6 @@ export const registerServices = async (): Promise<void> => {
api.registerService(new Presence());
api.registerService(new Authorization());

// TODO: Add it to a proper place since it's EE only
const { FederationMatrix } = await import('@rocket.chat/federation-matrix');
const federationMatrix = await FederationMatrix.create();
api.registerService(federationMatrix);
await registerFederationRoutes(federationMatrix);

// Run EE services defined outside of the main repo
// Otherwise, monolith would ignore them :(
// Always register the service and manage licensing inside the service (tbd)
Expand Down
2 changes: 2 additions & 0 deletions apps/meteor/startRocketChat.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { startLicense } from './ee/app/license/server/startup';
import { registerEEBroker } from './ee/server';
import { startFederationService as startFederationMatrixService } from './ee/server/startup/federation';
import { startFederationService } from './ee/server/startup/services';

const loadBeforeLicense = async () => {
Expand All @@ -8,6 +9,7 @@ const loadBeforeLicense = async () => {

const loadAfterLicense = async () => {
await startFederationService();
await startFederationMatrixService();
};

export const startRocketChat = async () => {
Expand Down
2 changes: 2 additions & 0 deletions ee/apps/federation-service/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,9 @@
"@rocket.chat/emitter": "^0.31.25",
"@rocket.chat/federation-matrix": "workspace:^",
"@rocket.chat/http-router": "workspace:*",
"@rocket.chat/license": "workspace:^",
"@rocket.chat/models": "workspace:*",
"@rocket.chat/network-broker": "workspace:^",
"hono": "^3.11.0",
"pino": "^8.16.0",
"polka": "^0.5.2",
Expand Down
37 changes: 30 additions & 7 deletions ee/apps/federation-service/src/service.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import 'reflect-metadata';
import { serve } from '@hono/node-server';
import { api, getConnection, getTrashCollection } from '@rocket.chat/core-services';
import { api, getConnection, getTrashCollection, Settings } from '@rocket.chat/core-services';
import { License } from '@rocket.chat/license';
import { registerServiceModels } from '@rocket.chat/models';
import { startBroker } from '@rocket.chat/network-broker';
import { Hono } from 'hono';
Expand All @@ -10,10 +11,19 @@ import { config } from './config';
function handleHealthCheck(app: Hono) {
app.get('/health', async (c) => {
try {
return c.json({ status: 'ok' });
const hasLicense = await License.hasModule('federation');
const isEnabled = await Settings.get('Federation_Service_Enabled');

return c.json({
status: 'ok',
license: hasLicense ? 'valid' : 'invalid',
settings: {
federation_enabled: isEnabled,
},
});
} catch (err) {
console.error('Service not healthy', err);
return c.json({ status: 'not healthy' }, 500);
return c.json({ status: 'not healthy', error: (err as Error).message }, 500);
}
});
}
Expand All @@ -26,6 +36,18 @@ function handleHealthCheck(app: Hono) {

api.setBroker(startBroker());

await api.start();

const hasLicense = License.hasModule('federation');
if (!hasLicense) {
throw new Error('Service requires a valid Enterprise license with the federation module');
}

const isEnabled = await Settings.get('Federation_Service_Enabled');
if (!isEnabled) {
throw new Error('Service is disabled in settings (Federation_Service_Enabled = false)');
}

const { FederationMatrix } = await import('@rocket.chat/federation-matrix');
const federationMatrix = await FederationMatrix.create();
api.registerService(federationMatrix);
Expand All @@ -35,13 +57,14 @@ function handleHealthCheck(app: Hono) {

app.mount('/_matrix', matrix.getHonoRouter().fetch);
app.mount('/.well-known', wellKnown.getHonoRouter().fetch);

handleHealthCheck(app);

serve({
fetch: app.fetch,
port: config.port,
});

await api.start();
})();
})().catch((error) => {
console.error('Failed to start service:', error);
process.exit(1);
});
2 changes: 2 additions & 0 deletions packages/core-services/src/LocalBroker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,8 @@ export class LocalBroker implements IBroker {
}
instance.removeAllListeners();
await instance.stopped();

this.services.delete(namespace);
}

/**
Expand Down
2 changes: 2 additions & 0 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -9084,7 +9084,9 @@ __metadata:
"@rocket.chat/emitter": "npm:^0.31.25"
"@rocket.chat/federation-matrix": "workspace:^"
"@rocket.chat/http-router": "workspace:*"
"@rocket.chat/license": "workspace:^"
"@rocket.chat/models": "workspace:*"
"@rocket.chat/network-broker": "workspace:^"
"@types/bun": "npm:latest"
"@types/express": "npm:^4.17.17"
hono: "npm:^3.11.0"
Expand Down
Loading