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
24 changes: 7 additions & 17 deletions apps/meteor/ee/server/api/federation.ts
Original file line number Diff line number Diff line change
@@ -1,15 +1,14 @@
import type { IFederationMatrixService } from '@rocket.chat/core-services';
import { FederationMatrix } from '@rocket.chat/core-services';
import { getFederationRoutes } from '@rocket.chat/federation-matrix';
import { Logger } from '@rocket.chat/logger';
import { ajv } from '@rocket.chat/rest-typings';
import type express from 'express';
import { WebApp } from 'meteor/webapp';

import { API } from '../../../app/api/server';
import { isRunningMs } from '../../../server/lib/isRunningMs';

const logger = new Logger('FederationRoutes');

let federationService: IFederationMatrixService | undefined;
API.v1.get(
'/federation/matrixIds.verify',
{
Expand All @@ -35,28 +34,19 @@ API.v1.get(
},
async function () {
const { matrixIds } = this.queryParams;
if (!federationService) {
throw new Error('Federation service not registered');
}
return API.v1.success({
results: await federationService.verifyMatrixIds(matrixIds),
results: await FederationMatrix.verifyMatrixIds(matrixIds),
});
},
);

export async function registerFederationRoutes(f: IFederationMatrixService): Promise<void> {
federationService = f;
if (isRunningMs()) {
return;
}

export async function registerFederationRoutes(): Promise<void> {
try {
const routes = federationService.getAllRoutes();
(WebApp.rawConnectHandlers as unknown as ReturnType<typeof express>).use(routes.matrix.router).use(routes.wellKnown.router);
const routes = getFederationRoutes();

logger.log('[Federation] Registered federation routes');
(WebApp.rawConnectHandlers as unknown as ReturnType<typeof express>).use(routes.matrix.router).use(routes.wellKnown.router);
} catch (error) {
logger.error('[Federation] Failed to register routes:', error);
logger.error({ msg: '[Federation] Failed to register routes:', err: error });
throw error;
}
}
124 changes: 12 additions & 112 deletions apps/meteor/ee/server/startup/federation.ts
Original file line number Diff line number Diff line change
@@ -1,135 +1,35 @@
import { api } from '@rocket.chat/core-services';
import { FederationMatrix } from '@rocket.chat/federation-matrix';
import { api, FederationMatrix as FederationMatrixService } from '@rocket.chat/core-services';
import { FederationMatrix, setupFederationMatrix } from '@rocket.chat/federation-matrix';
import { InstanceStatus } from '@rocket.chat/instance-status';
import { License } from '@rocket.chat/license';
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');

// TODO: should validate if the domain is resolving to us or not correctly
// should use homeserver.getFinalSomethingSomething and validate final Host header to have siteUrl
// this is a minimum sanity check to avoid full urls instead of the expected domain part
function validateDomain(domain: string): boolean {
const value = domain.trim();

if (!value) {
logger.error('The Federation domain is not set');
return false;
}

if (value.toLowerCase() !== value) {
logger.error(`The Federation domain "${value}" cannot have uppercase letters`);
return false;
}

export const startFederationService = async (): Promise<void> => {
try {
const valid = new URL(`https://${value}`).hostname === value;

if (!valid) {
throw new Error();
}
} catch {
logger.error(`The configured Federation domain "${value}" is not valid`);
return false;
}

return true;
}
const isEnabled = await setupFederationMatrix(InstanceStatus.id());

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

const shouldStartService = (): boolean => {
const hasLicense = License.hasModule('federation');
const isEnabled = settings.get('Federation_Service_Enabled') === true;
const domain = settings.get<string>('Federation_Service_Domain');
const hasDomain = validateDomain(domain);
return hasLicense && isEnabled && hasDomain;
};
await registerFederationRoutes();

const startService = async (): Promise<void> => {
if (federationMatrixService) {
logger.debug('Federation-matrix service already started... skipping');
// only registers the typing listener if the service is enabled
if (!isEnabled) {
return;
}

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

// TODO move to service/setup?
StreamerCentral.on('broadcast', (name, eventName, args) => {
if (!federationMatrixService) {
return;
}
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'));
void FederationMatrixService.notifyUserTyping(rid, user, activity.includes('user-typing'));
}
});

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();
} catch (error) {
logger.error('Failed to start federation-matrix service:', error);
}

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();
}
});

settings.watch<string>('Federation_Service_Domain', async (domain) => {
logger.debug('Federation_Service_Domain setting changed:', domain);
if (shouldStartService()) {
if (domain.toLowerCase() !== federationMatrixService?.getServerName().toLowerCase()) {
await stopService();
}
await startService();
} else {
await stopService();
}
});
};
1 change: 0 additions & 1 deletion apps/meteor/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -255,7 +255,6 @@
"@rocket.chat/emitter": "~0.31.25",
"@rocket.chat/favicon": "workspace:^",
"@rocket.chat/federation-matrix": "workspace:^",
"@rocket.chat/federation-service": "workspace:^",
"@rocket.chat/freeswitch": "workspace:^",
"@rocket.chat/fuselage": "^0.66.4",
"@rocket.chat/fuselage-forms": "^0.1.0",
Expand Down
4 changes: 0 additions & 4 deletions ee/apps/federation-service/.eslintrc.json

This file was deleted.

33 changes: 0 additions & 33 deletions ee/apps/federation-service/CHANGELOG.md

This file was deleted.

53 changes: 0 additions & 53 deletions ee/apps/federation-service/package.json

This file was deleted.

3 changes: 0 additions & 3 deletions ee/apps/federation-service/src/config.ts

This file was deleted.

71 changes: 0 additions & 71 deletions ee/apps/federation-service/src/service.ts

This file was deleted.

19 changes: 0 additions & 19 deletions ee/apps/federation-service/tsconfig.json

This file was deleted.

Loading
Loading