Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
34 commits
Select commit Hold shift + click to select a range
cae8103
add new routes required for federation bridges
sampaiodiego May 6, 2026
88b10b0
expose a way to save federation messages
sampaiodiego May 8, 2026
22cb49f
create room with alias
sampaiodiego May 8, 2026
833004f
fix bridge users not as local
sampaiodiego May 8, 2026
96adb1e
support join without invite for public rooms
sampaiodiego May 8, 2026
d492d65
add xmpp slash command to join rooms
sampaiodiego May 8, 2026
1bc6a41
fix typing
sampaiodiego May 18, 2026
db2c6f1
save user displayname
sampaiodiego May 25, 2026
963cdd8
add file upload support
sampaiodiego May 25, 2026
8a00424
send files
sampaiodiego May 25, 2026
3dc6e09
add profile routes for specific fields
sampaiodiego May 25, 2026
9aabb2a
add valid response to /client/versions
sampaiodiego May 27, 2026
a9ba13c
use standard logger instance for Matrix APIs
sampaiodiego May 27, 2026
8fcc104
add read receipt capability
sampaiodiego May 27, 2026
8c602bd
setup xmpp via settings
sampaiodiego Jun 1, 2026
8c623e4
remove check hack
sampaiodiego Jun 1, 2026
6d45a5d
cleanup slashcommand
sampaiodiego Jun 1, 2026
6c0a4bd
chore: bump federation-sdk to version 0.7.0-beta.0
sampaiodiego Jun 2, 2026
95cf3c3
chore: update federation-sdk to version 0.7.0-beta.1
sampaiodiego Jun 2, 2026
033e5e6
fix usernames from xmpp
sampaiodiego Jun 16, 2026
e0ac308
bump federation-sdk to 0.7.0-beta.3
sampaiodiego Jun 16, 2026
3d4392d
better error logging
sampaiodiego Jun 16, 2026
a423a17
add error handling to xmpp slash command
sampaiodiego Jun 17, 2026
40217d5
fix profile displayname set
sampaiodiego Jun 18, 2026
1903ff5
always show the name as typing
sampaiodiego Jun 18, 2026
9b9f05a
change variable name to better reflect its actual value
sampaiodiego Jun 18, 2026
ee84a7f
cleanup not used code
sampaiodiego Jun 25, 2026
30e58ad
remove catchall route
sampaiodiego Jun 25, 2026
a621ad1
better error return
sampaiodiego Jun 25, 2026
d611e6f
add support to admin/bridge room
sampaiodiego Jun 25, 2026
aebff51
update settings' descriptions
sampaiodiego Jun 25, 2026
925f9af
add support to ping events
sampaiodiego Jun 26, 2026
a540234
bump federation-sdk to 0.7.0-beta.4
sampaiodiego Jun 26, 2026
daf39dd
fix i18n
sampaiodiego Jun 26, 2026
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
12 changes: 7 additions & 5 deletions apps/meteor/app/file-upload/server/lib/FileUpload.ts
Original file line number Diff line number Diff line change
Expand Up @@ -139,20 +139,22 @@ export const FileUpload = {
},

async validateFileUpload(file: IUpload, content?: Buffer | string) {
if (!Match.test(file.rid, String)) {
const isFederationUpload = Boolean(file.federation?.mxcUri);

if (!isFederationUpload && !Match.test(file.rid, String)) {
return false;
}

// livechat users can upload files but they don't have an userId
const user = (file.userId && (await Users.findOne(file.userId))) || undefined;

const room = await Rooms.findOneById(file.rid);
if (!room) {
const room = file.rid ? await Rooms.findOneById(file.rid) : undefined;
if (!isFederationUpload && !room) {
return false;
}
const directMessageAllowed = settings.get('FileUpload_Enabled_Direct');
const fileUploadAllowed = settings.get('FileUpload_Enabled');
if (user?.type !== 'app' && (await canAccessRoomAsync(room, user, file)) !== true) {
if (!isFederationUpload && room && user?.type !== 'app' && (await canAccessRoomAsync(room, user, file)) !== true) {
return false;
}
const language = user?.language || 'en';
Expand All @@ -161,7 +163,7 @@ export const FileUpload = {
throw new Meteor.Error('error-file-upload-disabled', reason);
}

if (!directMessageAllowed && room.t === 'd') {
if (room && !directMessageAllowed && room.t === 'd') {
const reason = i18n.t('File_not_allowed_direct_messages', { lng: language });
throw new Meteor.Error('error-direct-message-file-upload-not-allowed', reason);
}
Expand Down
9 changes: 9 additions & 0 deletions apps/meteor/client/startup/slashCommands/federation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,3 +18,12 @@ slashCommands.add({
previewer,
previewCallback,
});

slashCommands.add({
command: 'xmpp',
options: {
description: 'Join xmpp rooms',
params: '#channel',
},
providesPreview: false,
});
67 changes: 61 additions & 6 deletions apps/meteor/ee/server/startup/federation.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,14 @@
import { api, FederationMatrix as FederationMatrixService } from '@rocket.chat/core-services';
import type { SlashCommandCallbackParams } from '@rocket.chat/core-typings';
import { FederationMatrix, configureFederationMatrixSettings, 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 { Users } from '@rocket.chat/models';

import { settings } from '../../../app/settings/server';
import { slashCommands } from '../../../app/utils/server/slashCommand';
import { i18n } from '../../../server/lib/i18n';
import { StreamerCentral } from '../../../server/modules/streamer/streamer.module';
import { registerFederationRoutes } from '../api/federation';

Expand All @@ -20,7 +24,7 @@ const configureFederation = async () => {
}

try {
configureFederationMatrixSettings({
await configureFederationMatrixSettings({
instanceId: InstanceStatus.id(),
domain: settings.get('Federation_Service_Domain'),
signingKey: settings.get('Federation_Service_Matrix_Signing_Key'),
Expand All @@ -31,6 +35,10 @@ const configureFederation = async () => {
processEDUTyping: settings.get('Federation_Service_EDU_Process_Typing'),
processEDUPresence: settings.get('Federation_Service_EDU_Process_Presence'),
processEDUReceipt: settings.get('Federation_Service_EDU_Process_Receipt'),
xmppEnabled: settings.get('Federation_XMPP_Enabled'),
xmppBridgeURL: settings.get('Federation_XMPP_Bridge_URL'),
xmppBridgeHSToken: settings.get('Federation_XMPP_Bridge_HS_Token'),
xmppBridgeASToken: settings.get('Federation_XMPP_Bridge_AS_Token'),
});
} catch (err) {
logger.error({ msg: 'Failed to start federation-matrix service', err });
Expand All @@ -55,6 +63,16 @@ export const startFederationService = async (): Promise<void> => {
}
});

// `setupFederationMatrix()` runs the SDK's `init()`, which registers the DB
// collections (including `AppServiceStateCollection`). It must complete
// before the settings watcher below, whose initial fire calls `setConfig`
// and resolves repositories that depend on those collections.
try {
await setupFederationMatrix();
} catch (err) {
logger.error({ msg: 'Failed to setup federation-matrix:', err });
}

settings.watchMultiple(
[
'Federation_Service_Enabled',
Expand All @@ -67,15 +85,52 @@ export const startFederationService = async (): Promise<void> => {
'Federation_Service_Matrix_Signing_Version',
'Federation_Service_Join_Encrypted_Rooms',
'Federation_Service_Join_Non_Private_Rooms',
'Federation_XMPP_Enabled',
'Federation_XMPP_Bridge_URL',
'Federation_XMPP_Bridge_HS_Token',
'Federation_XMPP_Bridge_AS_Token',
],
async () => {
await configureFederation();
},
);

try {
await setupFederationMatrix();
} catch (err) {
logger.error({ msg: 'Failed to setup federation-matrix:', err });
}
slashCommands.add({
command: 'xmpp',
callback: async ({ params, message, userId }: SlashCommandCallbackParams<'xmpp'>): Promise<void> => {
// the helper advertises `#channel`, so accept the leading # and strip it before joining
const channel = params.trim().replace(/^#/, '');
if (!channel) {
void api.broadcast('notify.ephemeralMessage', userId, message.rid, {
msg: i18n.t('Federation_XMPP_Join_Channel_Required', {
lng: settings.get('Language') || 'en',
}),
});
return;
}

const user = await Users.findOneById(userId);
if (!user) {
logger.error({ msg: 'User not found for joining xmpp room', userId });
return;
}

const joined = await FederationMatrixService.joinXMPPChatRoom(channel, user);

const lng = settings.get<string>('Language') || 'en';
if (joined) {
void api.broadcast('notify.ephemeralMessage', userId, message.rid, {
msg: `${i18n.t('Federation_XMPP_Join_Channel_Success', { lng })}`,
});
} else {
void api.broadcast('notify.ephemeralMessage', userId, message.rid, {
msg: `${i18n.t('Federation_XMPP_Join_Channel_Failed', { lng })}`,
});
}
},
options: {
description: 'Join xmpp rooms',
params: '#channel',
},
});
};
2 changes: 1 addition & 1 deletion apps/meteor/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,7 @@
"@rocket.chat/emitter": "^0.32.0",
"@rocket.chat/favicon": "workspace:^",
"@rocket.chat/federation-matrix": "workspace:^",
"@rocket.chat/federation-sdk": "0.6.3",
"@rocket.chat/federation-sdk": "0.7.0-beta.4",
"@rocket.chat/fuselage": "^0.79.1",
"@rocket.chat/fuselage-forms": "^1.3.0",
"@rocket.chat/fuselage-hooks": "^0.41.0",
Expand Down
4 changes: 2 additions & 2 deletions apps/meteor/server/services/upload/service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,9 +27,9 @@ const logger = new Logger('UploadService');
export class UploadService extends ServiceClassInternal implements IUploadService {
protected name = 'upload';

async uploadFile({ buffer, details }: IUploadFileParams): Promise<IUpload> {
async uploadFile({ buffer, details, federation }: IUploadFileParams): Promise<IUpload> {
const fileStore = FileUpload.getStore('Uploads');
return fileStore.insert(details, buffer);
return fileStore.insert({ ...details, ...(federation && { federation }) }, buffer);
}

async sendFileMessage({ roomId, file, userId, message }: ISendFileMessageParams): Promise<boolean | undefined> {
Expand Down
35 changes: 35 additions & 0 deletions apps/meteor/server/settings/federation-service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -116,5 +116,40 @@ export const createFederationServiceSettings = async (): Promise<void> => {
modules: ['federation'],
invalidValue: false,
});

await this.section('XMPP', async function () {
await this.add('Federation_XMPP_Enabled', false, {
type: 'boolean',
enterprise: true,
modules: ['federation'],
i18nLabel: 'Enabled',
invalidValue: false,
enableQuery: { _id: 'Federation_Service_Enabled', value: true },
});

await this.add('Federation_XMPP_Bridge_URL', '', {
type: 'string',
enterprise: true,
modules: ['federation'],
invalidValue: '',
enableQuery: { _id: 'Federation_XMPP_Enabled', value: true },
});

await this.add('Federation_XMPP_Bridge_HS_Token', '', {
type: 'password',
enterprise: true,
modules: ['federation'],
invalidValue: '',
enableQuery: { _id: 'Federation_Service_Enabled', value: true },
});

await this.add('Federation_XMPP_Bridge_AS_Token', '', {
type: 'password',
enterprise: true,
modules: ['federation'],
invalidValue: '',
enableQuery: { _id: 'Federation_Service_Enabled', value: true },
});
});
});
};
2 changes: 1 addition & 1 deletion ee/packages/federation-matrix/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@
"@rocket.chat/core-services": "workspace:^",
"@rocket.chat/core-typings": "workspace:^",
"@rocket.chat/emitter": "^0.32.0",
"@rocket.chat/federation-sdk": "0.6.3",
"@rocket.chat/federation-sdk": "0.7.0-beta.4",
"@rocket.chat/http-router": "workspace:^",
"@rocket.chat/license": "workspace:^",
"@rocket.chat/models": "workspace:^",
Expand Down
Loading
Loading