diff --git a/packages/federation-sdk/src/container.ts b/packages/federation-sdk/src/container.ts deleted file mode 100644 index 95e51868c..000000000 --- a/packages/federation-sdk/src/container.ts +++ /dev/null @@ -1,142 +0,0 @@ -import 'reflect-metadata'; - -import type { Emitter } from '@rocket.chat/emitter'; -import type { - EventStagingStore, - EventStore, -} from '@rocket.chat/federation-core'; -import type { Collection } from 'mongodb'; -import { container } from 'tsyringe'; - -import type { HomeserverEventSignatures } from './index'; -import { StagingAreaListener } from './listeners/staging-area.listener'; -import { StagingAreaQueue } from './queues/staging-area.queue'; -import { EventStagingRepository } from './repositories/event-staging.repository'; -import { EventRepository } from './repositories/event.repository'; -import { Key, KeyRepository } from './repositories/key.repository'; -import { Lock, LockRepository } from './repositories/lock.repository'; -import { Room, RoomRepository } from './repositories/room.repository'; -import { Server, ServerRepository } from './repositories/server.repository'; -import { - StateGraphRepository, - StateGraphStore, -} from './repositories/state-graph.repository'; -import { Upload, UploadRepository } from './repositories/upload.repository'; -import { ConfigService } from './services/config.service'; -import { DatabaseConnectionService } from './services/database-connection.service'; -import { EduService } from './services/edu.service'; -import { EventAuthorizationService } from './services/event-authorization.service'; -import { EventEmitterService } from './services/event-emitter.service'; -import { EventFetcherService } from './services/event-fetcher.service'; -import { EventService } from './services/event.service'; -import { FederationRequestService } from './services/federation-request.service'; -import { FederationService } from './services/federation.service'; -import { InviteService } from './services/invite.service'; -import { MediaService } from './services/media.service'; -import { MessageService } from './services/message.service'; -import { MissingEventService } from './services/missing-event.service'; -import { ProfilesService } from './services/profiles.service'; -import { RoomService } from './services/room.service'; -import { SendJoinService } from './services/send-join.service'; -import { ServerService } from './services/server.service'; -import { StagingAreaService } from './services/staging-area.service'; -import { StateService } from './services/state.service'; -import { WellKnownService } from './services/well-known.service'; - -export interface FederationContainerOptions { - emitter?: Emitter; -} - -export async function createFederationContainer( - options: FederationContainerOptions, - configInstance: ConfigService, -) { - const { emitter } = options; - - container.register(ConfigService, { - useValue: configInstance, - }); - - container.registerSingleton(DatabaseConnectionService); - const dbConnection = container.resolve(DatabaseConnectionService); - const db = await dbConnection.getDb(); - - container.registerSingleton(StagingAreaQueue); - - container.register>('EventCollection', { - useValue: db.collection('rocketchat_federation_events'), - }); - - container.register>('EventStagingCollection', { - useValue: db.collection( - 'rocketchat_federation_events_staging', - ), - }); - - container.register>('KeyCollection', { - useValue: db.collection('rocketchat_federation_keys'), - }); - - container.register>('LockCollection', { - useValue: db.collection('rocketchat_federation_locks'), - }); - - container.register>('RoomCollection', { - useValue: db.collection('rocketchat_federation_rooms'), - }); - - container.register>('ServerCollection', { - useValue: db.collection('rocketchat_federation_servers'), - }); - - container.register>('UploadCollection', { - useValue: db.collection('rocketchat_uploads'), - }); - - container.register>('StateGraphCollection', { - useValue: db.collection( - 'rocketchat_federation_state_graphs', - ), - }); - - container.registerSingleton(EventRepository); - container.registerSingleton(EventStagingRepository); - container.registerSingleton(KeyRepository); - container.registerSingleton(LockRepository); - container.registerSingleton(RoomRepository); - container.registerSingleton(ServerRepository); - container.registerSingleton(UploadRepository); - container.registerSingleton(StateGraphRepository); - - container.registerSingleton(FederationRequestService); - container.registerSingleton(FederationService); - container.registerSingleton(StateService); - container.registerSingleton(EventService); - container.registerSingleton(EventFetcherService); - container.registerSingleton(EventAuthorizationService); - container.registerSingleton(EventEmitterService); - container.registerSingleton(InviteService); - container.registerSingleton(MediaService); - container.registerSingleton(MessageService); - container.registerSingleton(MissingEventService); - container.registerSingleton(ProfilesService); - container.registerSingleton(RoomService); - container.registerSingleton(ServerService); - container.registerSingleton(WellKnownService); - container.registerSingleton(SendJoinService); - container.registerSingleton(StagingAreaService); - container.registerSingleton(EduService); - - container.registerSingleton(StagingAreaListener); - - const eventEmitterService = container.resolve(EventEmitterService); - if (emitter) { - eventEmitterService.setEmitter(emitter); - } else { - eventEmitterService.initializeStandalone(); - } - - container.resolve(StagingAreaListener); - - return container; -} diff --git a/packages/federation-sdk/src/federation.module.ts b/packages/federation-sdk/src/federation.module.ts deleted file mode 100644 index 959199625..000000000 --- a/packages/federation-sdk/src/federation.module.ts +++ /dev/null @@ -1,28 +0,0 @@ -import { ConfigService } from './services/config.service'; -import { EduService } from './services/edu.service'; -import { FederationRequestService } from './services/federation-request.service'; -import { FederationService } from './services/federation.service'; -import { InviteService } from './services/invite.service'; - -export class FederationModule { - static forRootAsync(options: Record) { - return { - module: FederationModule, - imports: options.imports || [], - providers: [ - ConfigService, - FederationService, - FederationRequestService, - EduService, - InviteService, - ], - exports: [ - FederationService, - FederationRequestService, - ConfigService, - EduService, - InviteService, - ], - }; - } -} diff --git a/packages/federation-sdk/src/index.ts b/packages/federation-sdk/src/index.ts index 369758c66..0d00e9cd5 100644 --- a/packages/federation-sdk/src/index.ts +++ b/packages/federation-sdk/src/index.ts @@ -1,21 +1,28 @@ -import type { Membership, MessageType } from '@rocket.chat/federation-core'; -import type { EventID, PduForType } from '@rocket.chat/federation-room'; +import 'reflect-metadata'; + +import type { Emitter } from '@rocket.chat/emitter'; +import type { + EventStagingStore, + Membership, + MessageType, +} from '@rocket.chat/federation-core'; +import type { + EventID, + EventStore, + PduForType, +} from '@rocket.chat/federation-room'; +import { Collection } from 'mongodb'; import { container } from 'tsyringe'; -import { ConfigService } from './services/config.service'; -import { EduService } from './services/edu.service'; -import { EventAuthorizationService } from './services/event-authorization.service'; +import { StagingAreaListener } from './listeners/staging-area.listener'; +import { Key } from './repositories/key.repository'; +import { Lock } from './repositories/lock.repository'; +import { Room } from './repositories/room.repository'; +import { Server } from './repositories/server.repository'; +import { StateGraphStore } from './repositories/state-graph.repository'; +import { Upload } from './repositories/upload.repository'; +import { FederationSDK } from './sdk'; +import { DatabaseConnectionService } from './services/database-connection.service'; import { EventEmitterService } from './services/event-emitter.service'; -import { EventService } from './services/event.service'; -import { FederationRequestService } from './services/federation-request.service'; -import { InviteService } from './services/invite.service'; -import { MediaService } from './services/media.service'; -import { MessageService } from './services/message.service'; -import { ProfilesService } from './services/profiles.service'; -import { RoomService } from './services/room.service'; -import { SendJoinService } from './services/send-join.service'; -import { ServerService } from './services/server.service'; -import { StateService } from './services/state.service'; -import { WellKnownService } from './services/well-known.service'; export type { Pdu, @@ -38,7 +45,6 @@ export type { } from '@rocket.chat/federation-core'; export { generateEd25519RandomSecretKey } from '@rocket.chat/federation-crypto'; -export { FederationEndpoints } from './specs/federation-api'; export type { MakeJoinResponse, SendJoinResponse, @@ -49,38 +55,6 @@ export type { Version, } from './specs/federation-api'; -export { FederationModule } from './federation.module'; - -export { FederationRequestService } from './services/federation-request.service'; -export { FederationService } from './services/federation.service'; -export { WellKnownService } from './services/well-known.service'; -export { ConfigService } from './services/config.service'; -export type { AppConfig } from './services/config.service'; -export { DatabaseConnectionService } from './services/database-connection.service'; -export { EduService } from './services/edu.service'; - -export { ServerService } from './services/server.service'; -export { EventAuthorizationService } from './services/event-authorization.service'; -export { MissingEventService } from './services/missing-event.service'; -export { ProfilesService } from './services/profiles.service'; -export { EventFetcherService } from './services/event-fetcher.service'; -export type { FetchedEvents } from './services/event-fetcher.service'; -export { InviteService } from './services/invite.service'; -export type { ProcessInviteEvent } from './services/invite.service'; -export { MessageService } from './services/message.service'; -export { EventService } from './services/event.service'; -export { RoomService } from './services/room.service'; -export { StateService } from './services/state.service'; -export { StagingAreaService } from './services/staging-area.service'; -export { SendJoinService } from './services/send-join.service'; -export { EventEmitterService } from './services/event-emitter.service'; -export { MediaService } from './services/media.service'; -// Repository interfaces and implementations - -// Queue implementations -export { BaseQueue, type QueueHandler } from './queues/base.queue'; -export { StagingAreaQueue } from './queues/staging-area.queue'; - // Utility exports export { getErrorMessage } from './utils/get-error-message'; export { USERNAME_REGEX, ROOM_ID_REGEX } from './utils/validation-regex'; @@ -92,29 +66,6 @@ export { export { errCodes } from './utils/response-codes'; export { NotAllowedError } from './services/invite.service'; -export { EventRepository } from './repositories/event.repository'; -export { RoomRepository } from './repositories/room.repository'; -export { ServerRepository } from './repositories/server.repository'; -export { KeyRepository } from './repositories/key.repository'; - -export interface HomeserverServices { - room: RoomService; - message: MessageService; - event: EventService; - invite: InviteService; - wellKnown: WellKnownService; - profile: ProfilesService; - state: StateService; - sendJoin: SendJoinService; - server: ServerService; - config: ConfigService; - edu: EduService; - media: MediaService; - request: FederationRequestService; - federationAuth: EventAuthorizationService; - emitter: EventEmitterService; -} - type RelatesTo = | { rel_type: 'm.replace'; @@ -304,37 +255,71 @@ export type HomeserverEventSignatures = { }; }; -export function getAllServices(): HomeserverServices { - return { - room: container.resolve(RoomService), - message: container.resolve(MessageService), - event: container.resolve(EventService), - invite: container.resolve(InviteService), - wellKnown: container.resolve(WellKnownService), - profile: container.resolve(ProfilesService), - state: container.resolve(StateService), - sendJoin: container.resolve(SendJoinService), - server: container.resolve(ServerService), - config: container.resolve(ConfigService), - edu: container.resolve(EduService), - media: container.resolve(MediaService), - request: container.resolve(FederationRequestService), - federationAuth: container.resolve(EventAuthorizationService), - emitter: container.resolve(EventEmitterService), - }; -} - -export { StagingAreaListener } from './listeners/staging-area.listener'; - -export { - createFederationContainer, - type FederationContainerOptions, -} from './container'; - -export { DependencyContainer } from 'tsyringe'; - export { roomIdSchema, userIdSchema, eventIdSchema, } from '@rocket.chat/federation-room'; + +export async function init({ + emitter, + dbConfig, +}: { + emitter?: Emitter; + dbConfig: { + uri: string; + name: string; + poolSize: number; + }; +}) { + const dbConnection = new DatabaseConnectionService(dbConfig); + const db = await dbConnection.getDb(); + + container.register>('EventCollection', { + useValue: db.collection('rocketchat_federation_events'), + }); + + container.register>('EventStagingCollection', { + useValue: db.collection( + 'rocketchat_federation_events_staging', + ), + }); + + container.register>('KeyCollection', { + useValue: db.collection('rocketchat_federation_keys'), + }); + + container.register>('LockCollection', { + useValue: db.collection('rocketchat_federation_locks'), + }); + + container.register>('RoomCollection', { + useValue: db.collection('rocketchat_federation_rooms'), + }); + + container.register>('ServerCollection', { + useValue: db.collection('rocketchat_federation_servers'), + }); + + container.register>('UploadCollection', { + useValue: db.collection('rocketchat_uploads'), + }); + + container.register>('StateGraphCollection', { + useValue: db.collection( + 'rocketchat_federation_state_graphs', + ), + }); + + const eventEmitterService = container.resolve(EventEmitterService); + if (emitter) { + eventEmitterService.setEmitter(emitter); + } else { + eventEmitterService.initializeStandalone(); + } + + // this is required to initialize the listener and register the queue handler + container.resolve(StagingAreaListener); +} + +export const federationSDK = container.resolve(FederationSDK); diff --git a/packages/federation-sdk/src/sdk.ts b/packages/federation-sdk/src/sdk.ts new file mode 100644 index 000000000..66b967bbb --- /dev/null +++ b/packages/federation-sdk/src/sdk.ts @@ -0,0 +1,301 @@ +import type { EventStore } from '@rocket.chat/federation-core'; +import type { PduForType, PduType } from '@rocket.chat/federation-room'; +import { delay, inject, singleton } from 'tsyringe'; + +import { AppConfig, ConfigService } from './services/config.service'; +import { EduService } from './services/edu.service'; +import { EventAuthorizationService } from './services/event-authorization.service'; +import { EventService } from './services/event.service'; +import { FederationRequestService } from './services/federation-request.service'; +import { FederationService } from './services/federation.service'; +import { InviteService } from './services/invite.service'; +import { MediaService } from './services/media.service'; +import { MessageService } from './services/message.service'; +import { ProfilesService } from './services/profiles.service'; +import { RoomService } from './services/room.service'; +import { SendJoinService } from './services/send-join.service'; +import { ServerService } from './services/server.service'; +import { StateService } from './services/state.service'; +import { WellKnownService } from './services/well-known.service'; + +// create a federation sdk class to export +@singleton() +export class FederationSDK { + constructor( + @inject(delay(() => RoomService)) private readonly roomService: RoomService, + @inject(delay(() => MessageService)) + private readonly messageService: MessageService, + @inject(delay(() => InviteService)) + private readonly inviteService: InviteService, + @inject(delay(() => EventService)) + private readonly eventService: EventService, + @inject(delay(() => EduService)) private readonly eduService: EduService, + @inject(delay(() => ServerService)) + private readonly serverService: ServerService, + private readonly configService: ConfigService, + @inject(delay(() => EventAuthorizationService)) + private readonly eventAuthorizationService: EventAuthorizationService, + @inject(delay(() => StateService)) + private readonly stateService: StateService, + private readonly mediaService: MediaService, + @inject(delay(() => ProfilesService)) + private readonly profilesService: ProfilesService, + @inject(delay(() => SendJoinService)) + private readonly sendJoinService: SendJoinService, + private readonly wellKnownService: WellKnownService, + private readonly federationRequestService: FederationRequestService, + @inject(delay(() => FederationService)) + private readonly federationService: FederationService, + ) {} + + createDirectMessageRoom( + ...args: Parameters + ) { + return this.roomService.createDirectMessageRoom(...args); + } + + createRoom(...args: Parameters) { + return this.roomService.createRoom(...args); + } + + inviteUserToRoom( + ...args: Parameters + ) { + return this.inviteService.inviteUserToRoom(...args); + } + + sendFileMessage( + ...args: Parameters + ) { + return this.messageService.sendFileMessage(...args); + } + + sendMessage(...args: Parameters) { + return this.messageService.sendMessage(...args); + } + + redactMessage(...args: Parameters) { + return this.messageService.redactMessage(...args); + } + + sendReaction(...args: Parameters) { + return this.messageService.sendReaction(...args); + } + + unsetReaction(...args: Parameters) { + return this.messageService.unsetReaction(...args); + } + + getEventById>>( + ...args: Parameters + ): Promise

{ + return this.eventService.getEventById(...args); + } + + leaveRoom(...args: Parameters) { + return this.roomService.leaveRoom(...args); + } + + kickUser(...args: Parameters) { + return this.roomService.kickUser(...args); + } + + updateMessage(...args: Parameters) { + return this.messageService.updateMessage(...args); + } + + updateRoomName(...args: Parameters) { + return this.roomService.updateRoomName(...args); + } + + setRoomTopic(...args: Parameters) { + return this.roomService.setRoomTopic(...args); + } + + setPowerLevelForUser( + ...args: Parameters + ) { + return this.roomService.setPowerLevelForUser(...args); + } + + sendTypingNotification( + ...args: Parameters + ) { + return this.eduService.sendTypingNotification(...args); + } + + getSignedServerKey( + ...args: Parameters + ) { + return this.serverService.getSignedServerKey(...args); + } + + getConfig(config: K): AppConfig[K] { + return this.configService.getConfig(config); + } + + processInvite(...args: Parameters) { + return this.inviteService.processInvite(...args); + } + + verifyRequestSignature( + ...args: Parameters< + typeof this.eventAuthorizationService.verifyRequestSignature + > + ) { + return this.eventAuthorizationService.verifyRequestSignature(...args); + } + + joinUser(...args: Parameters) { + return this.roomService.joinUser(...args); + } + + getLatestRoomState2( + ...args: Parameters + ) { + return this.stateService.getLatestRoomState2(...args); + } + + downloadFromRemoteServer( + ...args: Parameters + ) { + return this.mediaService.downloadFromRemoteServer(...args); + } + + queryProfile(...args: Parameters) { + return this.profilesService.queryProfile(...args); + } + + getAllPublicRoomIdsAndNames( + ...args: Parameters + ) { + return this.stateService.getAllPublicRoomIdsAndNames(...args); + } + + sendJoin(...args: Parameters) { + return this.sendJoinService.sendJoin(...args); + } + + processIncomingTransaction( + ...args: Parameters + ) { + return this.eventService.processIncomingTransaction(...args); + } + + getStateIds(...args: Parameters) { + return this.eventService.getStateIds(...args); + } + + getState(...args: Parameters) { + return this.eventService.getState(...args); + } + + getBackfillEvents( + ...args: Parameters + ) { + return this.eventService.getBackfillEvents(...args); + } + + canAccessResource( + ...args: Parameters + ) { + return this.eventAuthorizationService.canAccessResource(...args); + } + + getWellKnownHostData( + ...args: Parameters + ) { + return this.wellKnownService.getWellKnownHostData(...args); + } + + updateUserPowerLevel( + ...args: Parameters + ) { + return this.roomService.updateUserPowerLevel(...args); + } + + findStateAtEvent( + ...args: Parameters + ) { + return this.stateService.findStateAtEvent(...args); + } + + getLatestRoomState( + ...args: Parameters + ) { + return this.stateService.getLatestRoomState(...args); + } + + handlePdu(...args: Parameters) { + return this.stateService.handlePdu(...args); + } + + markRoomAsTombstone( + ...args: Parameters + ) { + return this.roomService.markRoomAsTombstone(...args); + } + + getAllRoomIds(...args: Parameters) { + return this.stateService.getAllRoomIds(...args); + } + + makeSignedRequest( + ...args: Parameters + ) { + return this.federationRequestService.makeSignedRequest(...args); + } + + queryProfileRemote({ + homeserverUrl, + userId, + }: { homeserverUrl: string; userId: string }) { + return this.federationRequestService.get( + homeserverUrl, + '/_matrix/federation/v1/query/profile', + { user_id: userId }, + ); + } + + buildEvent( + ...args: Parameters> + ) { + return this.stateService.buildEvent(...args); + } + + sendEventToAllServersInRoom( + ...args: Parameters< + typeof this.federationService.sendEventToAllServersInRoom + > + ) { + return this.federationService.sendEventToAllServersInRoom(...args); + } + + makeJoin(...args: Parameters) { + return this.profilesService.makeJoin(...args); + } + + getMissingEvents( + ...args: Parameters + ) { + return this.profilesService.getMissingEvents(...args); + } + + eventAuth(...args: Parameters) { + return this.profilesService.eventAuth(...args); + } + + setConfig(...args: Parameters) { + return this.configService.setConfig(...args); + } + + queryKeys(...args: Parameters) { + return this.profilesService.queryKeys(...args); + } + + sendPresenceUpdateToRooms( + ...args: Parameters + ) { + return this.eduService.sendPresenceUpdateToRooms(...args); + } +} diff --git a/packages/federation-sdk/src/services/config.service.ts b/packages/federation-sdk/src/services/config.service.ts index 62cc9d2f3..bbb8de6b9 100644 --- a/packages/federation-sdk/src/services/config.service.ts +++ b/packages/federation-sdk/src/services/config.service.ts @@ -4,6 +4,7 @@ import { generateKeyPairsFromString, toUnpaddedBase64, } from '@rocket.chat/federation-core'; +import { singleton } from 'tsyringe'; import { z } from 'zod'; @@ -17,11 +18,6 @@ export interface AppConfig { signingKey?: string; timeout?: number; signingKeyPath?: string; - database: { - uri: string; - name: string; - poolSize: number; - }; media: { maxFileSize: number; allowedMimeTypes: string[]; @@ -35,6 +31,10 @@ export interface AppConfig { allowedEncryptedRooms: boolean; allowedNonPrivateRooms: boolean; }; + edu: { + processTyping: boolean; + processPresence: boolean; + }; } export const AppConfigSchema = z.object({ @@ -50,11 +50,6 @@ export const AppConfigSchema = z.object({ signingKey: z.string().optional(), timeout: z.number().optional(), signingKeyPath: z.string(), - database: z.object({ - uri: z.string().min(1, 'Database URI is required'), - name: z.string().min(1, 'Database name is required'), - poolSize: z.number().int().min(1, 'Pool size must be at least 1'), - }), media: z.object({ maxFileSize: z .number() @@ -77,14 +72,19 @@ export const AppConfigSchema = z.object({ allowedEncryptedRooms: z.boolean(), allowedNonPrivateRooms: z.boolean(), }), + edu: z.object({ + processTyping: z.boolean(), + processPresence: z.boolean(), + }), }); +@singleton() export class ConfigService { - private config: AppConfig; + private config: AppConfig = {} as AppConfig; private logger = createLogger('ConfigService'); private serverKeys: SigningKey[] = []; - constructor(values: AppConfig) { + setConfig(values: AppConfig) { try { this.config = AppConfigSchema.parse(values); } catch (error) { @@ -113,16 +113,8 @@ export class ConfigService { return this.config.instanceId; } - getDatabaseConfig(): AppConfig['database'] { - return this.config.database; - } - - getMediaConfig(): AppConfig['media'] { - return this.config.media; - } - - getInviteConfig(): AppConfig['invite'] { - return this.config.invite; + getConfig(config: K): AppConfig[K] { + return this.config[config]; } async getSigningKey() { diff --git a/packages/federation-sdk/src/services/database-connection.service.ts b/packages/federation-sdk/src/services/database-connection.service.ts index 1db76a3eb..17a4cc0ec 100644 --- a/packages/federation-sdk/src/services/database-connection.service.ts +++ b/packages/federation-sdk/src/services/database-connection.service.ts @@ -1,16 +1,15 @@ import { createLogger } from '@rocket.chat/federation-core'; import { Db, MongoClient, type MongoClientOptions } from 'mongodb'; -import { singleton } from 'tsyringe'; -import { ConfigService } from './config.service'; -@singleton() export class DatabaseConnectionService { private client: MongoClient | null = null; private db: Db | null = null; private connectionPromise: Promise | null = null; private readonly logger = createLogger('DatabaseConnectionService'); - constructor(private readonly configService: ConfigService) { + constructor( + private readonly config: { uri: string; name: string; poolSize: number }, + ) { this.connect().catch((err) => this.logger.error({ msg: 'Initial database connection failed', err }), ); @@ -39,7 +38,7 @@ export class DatabaseConnectionService { this.connectionPromise = new Promise((resolve, reject) => { try { - const dbConfig = this.configService.getDatabaseConfig(); + const dbConfig = this.config; const options: MongoClientOptions = { maxPoolSize: dbConfig.poolSize, diff --git a/packages/federation-sdk/src/services/federation-request.service.spec.ts b/packages/federation-sdk/src/services/federation-request.service.spec.ts index 4af81b0bb..83a056402 100644 --- a/packages/federation-sdk/src/services/federation-request.service.spec.ts +++ b/packages/federation-sdk/src/services/federation-request.service.spec.ts @@ -91,6 +91,12 @@ describe('FederationRequestService', async () => { ); configService = { + getConfig: (key: string) => { + if (key === 'serverName') { + return mockServerName; + } + throw new Error(`Unknown config key: ${key}`); + }, serverName: mockServerName, getSigningKeyBase64: async () => mockSigningKey, getSigningKeyId: async () => mockSigningKeyId, diff --git a/packages/federation-sdk/src/services/federation-request.service.ts b/packages/federation-sdk/src/services/federation-request.service.ts index cb71703ff..928e4765a 100644 --- a/packages/federation-sdk/src/services/federation-request.service.ts +++ b/packages/federation-sdk/src/services/federation-request.service.ts @@ -40,7 +40,7 @@ export class FederationRequestService { body, queryString, }: SignedRequest): Promise> { - const serverName = this.configService.serverName; + const serverName = this.configService.getConfig('serverName'); const signingKeyBase64 = await this.configService.getSigningKeyBase64(); const signingKeyId = await this.configService.getSigningKeyId(); const privateKeyBytes = Buffer.from(signingKeyBase64, 'base64'); diff --git a/packages/federation-sdk/src/services/federation.service.ts b/packages/federation-sdk/src/services/federation.service.ts index f6f1a7b80..38b381905 100644 --- a/packages/federation-sdk/src/services/federation.service.ts +++ b/packages/federation-sdk/src/services/federation.service.ts @@ -8,7 +8,7 @@ import { PersistentEventFactory, extractDomainFromId, } from '@rocket.chat/federation-room'; -import { singleton } from 'tsyringe'; +import { delay, inject, singleton } from 'tsyringe'; import { FederationEndpoints, type MakeJoinResponse, @@ -30,6 +30,7 @@ export class FederationService { private readonly requestService: FederationRequestService, + @inject(delay(() => StateService)) private readonly stateService: StateService, ) {} diff --git a/packages/federation-sdk/src/services/invite.service.ts b/packages/federation-sdk/src/services/invite.service.ts index 250d1c707..bd68fe49d 100644 --- a/packages/federation-sdk/src/services/invite.service.ts +++ b/packages/federation-sdk/src/services/invite.service.ts @@ -171,7 +171,7 @@ export class InviteService { ); const { allowedEncryptedRooms, allowedNonPrivateRooms } = - this.configService.getInviteConfig(); + this.configService.getConfig('invite'); const shouldRejectInvite = (!allowedEncryptedRooms && isRoomEncrypted) || diff --git a/packages/federation-sdk/src/services/state.service.spec.ts b/packages/federation-sdk/src/services/state.service.spec.ts index ec2242aab..0bc5f2504 100644 --- a/packages/federation-sdk/src/services/state.service.spec.ts +++ b/packages/federation-sdk/src/services/state.service.spec.ts @@ -126,11 +126,9 @@ describe('StateService', async () => { const configServiceInstance = { getSigningKey: async () => {}, serverName: 'example.com', - database: databaseConfig, - getDatabaseConfig: () => databaseConfig, } as unknown as ConfigService; - const database = new DatabaseConnectionService(configServiceInstance); + const database = new DatabaseConnectionService(databaseConfig); const eventCollection = (await database.getDb()).collection< WithId diff --git a/packages/federation-sdk/src/services/state.service.ts b/packages/federation-sdk/src/services/state.service.ts index 9492b3788..d1fe93237 100644 --- a/packages/federation-sdk/src/services/state.service.ts +++ b/packages/federation-sdk/src/services/state.service.ts @@ -66,7 +66,6 @@ export class StateService { private readonly logger = createLogger('StateService'); constructor( private readonly stateRepository: StateGraphRepository, - private readonly eventRepository: EventRepository, private readonly configService: ConfigService, ) {} diff --git a/packages/homeserver/src/controllers/federation/invite.controller.ts b/packages/homeserver/src/controllers/federation/invite.controller.ts index 2484f39fc..60967a86b 100644 --- a/packages/homeserver/src/controllers/federation/invite.controller.ts +++ b/packages/homeserver/src/controllers/federation/invite.controller.ts @@ -1,12 +1,7 @@ import { EventID, RoomID } from '@rocket.chat/federation-room'; -import { - EventAuthorizationService, - InviteService, - NotAllowedError, -} from '@rocket.chat/federation-sdk'; +import { NotAllowedError, federationSDK } from '@rocket.chat/federation-sdk'; import { isAuthenticatedMiddleware } from '@rocket.chat/homeserver/middlewares/isAuthenticated'; import { Elysia, t } from 'elysia'; -import { container } from 'tsyringe'; import { FederationErrorResponseDto, ProcessInviteParamsDto, @@ -15,10 +10,7 @@ import { } from '../../dtos'; export const invitePlugin = (app: Elysia) => { - const inviteService = container.resolve(InviteService); - const eventAuthService = container.resolve(EventAuthorizationService); - - return app.use(isAuthenticatedMiddleware(eventAuthService)).put( + return app.use(isAuthenticatedMiddleware()).put( '/_matrix/federation/v2/invite/:roomId/:eventId', async ({ body, set, params: { roomId, eventId }, authenticatedServer }) => { if (!authenticatedServer) { @@ -26,7 +18,7 @@ export const invitePlugin = (app: Elysia) => { } try { - return await inviteService.processInvite( + return await federationSDK.processInvite( body.event, roomId as RoomID, eventId as EventID, diff --git a/packages/homeserver/src/controllers/federation/media.controller.ts b/packages/homeserver/src/controllers/federation/media.controller.ts index 74259edf6..49b4f0315 100644 --- a/packages/homeserver/src/controllers/federation/media.controller.ts +++ b/packages/homeserver/src/controllers/federation/media.controller.ts @@ -1,8 +1,6 @@ -import { EventAuthorizationService } from '@rocket.chat/federation-sdk'; import { canAccessResourceMiddleware } from '@rocket.chat/homeserver/middlewares/canAccessResource'; import { isAuthenticatedMiddleware } from '@rocket.chat/homeserver/middlewares/isAuthenticated'; import { Elysia, t } from 'elysia'; -import { container } from 'tsyringe'; const ErrorResponseSchema = t.Object({ errcode: t.Literal('M_UNRECOGNIZED'), @@ -15,11 +13,9 @@ const ErrorResponseSchema = t.Object({ * All the medias are being handled by the Rocket.Chat instances. */ export const mediaPlugin = (app: Elysia) => { - const eventAuthService = container.resolve(EventAuthorizationService); - return app.group('/_matrix', (app) => app - .use(isAuthenticatedMiddleware(eventAuthService)) + .use(isAuthenticatedMiddleware()) .get( '/media/v3/config', async ({ set }) => { @@ -40,7 +36,7 @@ export const mediaPlugin = (app: Elysia) => { }, }, ) - .use(canAccessResourceMiddleware(eventAuthService, 'media')) + .use(canAccessResourceMiddleware('media')) .get( '/federation/v1/media/download/:mediaId', async ({ set }) => { diff --git a/packages/homeserver/src/controllers/federation/profiles.controller.ts b/packages/homeserver/src/controllers/federation/profiles.controller.ts index 37b550be4..2e94c3a2b 100644 --- a/packages/homeserver/src/controllers/federation/profiles.controller.ts +++ b/packages/homeserver/src/controllers/federation/profiles.controller.ts @@ -1,12 +1,8 @@ import { EventID, RoomID, UserID } from '@rocket.chat/federation-room'; -import { - EventAuthorizationService, - ProfilesService, -} from '@rocket.chat/federation-sdk'; +import { federationSDK } from '@rocket.chat/federation-sdk'; import { canAccessResourceMiddleware } from '@rocket.chat/homeserver/middlewares/canAccessResource'; import { isAuthenticatedMiddleware } from '@rocket.chat/homeserver/middlewares/isAuthenticated'; import { Elysia, t } from 'elysia'; -import { container } from 'tsyringe'; import { ErrorResponseDto, EventAuthParamsDto, @@ -26,17 +22,14 @@ import { } from '../../dtos'; export const profilesPlugin = (app: Elysia) => { - const profilesService = container.resolve(ProfilesService); - const eventAuthService = container.resolve(EventAuthorizationService); - return app .group('/_matrix', (app) => app - .use(isAuthenticatedMiddleware(eventAuthService)) + .use(isAuthenticatedMiddleware()) .get( '/federation/v1/query/profile', ({ query: { user_id } }) => - profilesService.queryProfile(user_id as UserID), + federationSDK.queryProfile(user_id as UserID), { query: QueryProfileQueryDto, response: { @@ -100,7 +93,7 @@ export const profilesPlugin = (app: Elysia) => { }, ), ) - .use(canAccessResourceMiddleware(eventAuthService, 'room')) + .use(canAccessResourceMiddleware('room')) .get( '/_matrix/federation/v1/make_join/:roomId/:userId', async ({ params, query: _query }) => { @@ -108,7 +101,7 @@ export const profilesPlugin = (app: Elysia) => { // const { ver } = query; - return profilesService.makeJoin(roomId as RoomID, userId as UserID, [ + return federationSDK.makeJoin(roomId as RoomID, userId as UserID, [ '10', ]); }, @@ -129,7 +122,7 @@ export const profilesPlugin = (app: Elysia) => { .post( '/_matrix/federation/v1/get_missing_events/:roomId', async ({ params, body }) => - profilesService.getMissingEvents( + federationSDK.getMissingEvents( params.roomId as RoomID, body.earliest_events as EventID[], body.latest_events as EventID[], @@ -152,7 +145,7 @@ export const profilesPlugin = (app: Elysia) => { .get( '/_matrix/federation/v1/event_auth/:roomId/:eventId', ({ params }) => - profilesService.eventAuth( + federationSDK.eventAuth( params.roomId as RoomID, params.eventId as EventID, ), diff --git a/packages/homeserver/src/controllers/federation/rooms.controller.ts b/packages/homeserver/src/controllers/federation/rooms.controller.ts index 90080988e..dcf5b70f1 100644 --- a/packages/homeserver/src/controllers/federation/rooms.controller.ts +++ b/packages/homeserver/src/controllers/federation/rooms.controller.ts @@ -1,17 +1,10 @@ -import { - EventAuthorizationService, - StateService, -} from '@rocket.chat/federation-sdk'; +import { federationSDK } from '@rocket.chat/federation-sdk'; import { isAuthenticatedMiddleware } from '@rocket.chat/homeserver/middlewares/isAuthenticated'; import { Elysia, t } from 'elysia'; -import { container } from 'tsyringe'; export const roomPlugin = (app: Elysia) => { - const stateService = container.resolve(StateService); - const eventAuthService = container.resolve(EventAuthorizationService); - return app - .use(isAuthenticatedMiddleware(eventAuthService)) + .use(isAuthenticatedMiddleware()) .get( '/_matrix/federation/v1/publicRooms', async ({ query }) => { @@ -24,7 +17,7 @@ export const roomPlugin = (app: Elysia) => { const { limit: _limit } = query; - const publicRooms = await stateService.getAllPublicRoomIdsAndNames(); + const publicRooms = await federationSDK.getAllPublicRoomIdsAndNames(); return { chunk: publicRooms.map((room) => ({ @@ -68,7 +61,7 @@ export const roomPlugin = (app: Elysia) => { const { filter } = body; - const publicRooms = await stateService.getAllPublicRoomIdsAndNames(); + const publicRooms = await federationSDK.getAllPublicRoomIdsAndNames(); return { chunk: publicRooms diff --git a/packages/homeserver/src/controllers/federation/send-join.controller.ts b/packages/homeserver/src/controllers/federation/send-join.controller.ts index f02dca526..b4d65b084 100644 --- a/packages/homeserver/src/controllers/federation/send-join.controller.ts +++ b/packages/homeserver/src/controllers/federation/send-join.controller.ts @@ -1,11 +1,7 @@ import type { EventID, RoomID } from '@rocket.chat/federation-room'; -import { - EventAuthorizationService, - SendJoinService, -} from '@rocket.chat/federation-sdk'; +import { federationSDK } from '@rocket.chat/federation-sdk'; import { canAccessResourceMiddleware } from '@rocket.chat/homeserver/middlewares/canAccessResource'; import { Elysia, t } from 'elysia'; -import { container } from 'tsyringe'; import { ErrorResponseDto, SendJoinEventDto, @@ -13,10 +9,7 @@ import { } from '../../dtos'; export const sendJoinPlugin = (app: Elysia) => { - const sendJoinService = container.resolve(SendJoinService); - const eventAuthService = container.resolve(EventAuthorizationService); - - return app.use(canAccessResourceMiddleware(eventAuthService, 'room')).put( + return app.use(canAccessResourceMiddleware('room')).put( '/_matrix/federation/v2/send_join/:roomId/:eventId', async ({ params, @@ -25,7 +18,7 @@ export const sendJoinPlugin = (app: Elysia) => { }) => { const { roomId, eventId } = params; - return sendJoinService.sendJoin( + return federationSDK.sendJoin( roomId as RoomID, eventId as EventID, body as any, diff --git a/packages/homeserver/src/controllers/federation/state.controller.ts b/packages/homeserver/src/controllers/federation/state.controller.ts index 33ece4bc3..d09df7e3e 100644 --- a/packages/homeserver/src/controllers/federation/state.controller.ts +++ b/packages/homeserver/src/controllers/federation/state.controller.ts @@ -1,11 +1,7 @@ import { EventID, RoomID } from '@rocket.chat/federation-room'; -import { - EventAuthorizationService, - EventService, -} from '@rocket.chat/federation-sdk'; +import { federationSDK } from '@rocket.chat/federation-sdk'; import { canAccessResourceMiddleware } from '@rocket.chat/homeserver/middlewares/canAccessResource'; import { Elysia } from 'elysia'; -import { container } from 'tsyringe'; import { ErrorResponseDto, GetStateIdsParamsDto, @@ -17,15 +13,12 @@ import { } from '../../dtos'; export const statePlugin = (app: Elysia) => { - const eventService = container.resolve(EventService); - const eventAuthService = container.resolve(EventAuthorizationService); - return app - .use(canAccessResourceMiddleware(eventAuthService, 'room')) + .use(canAccessResourceMiddleware('room')) .get( '/_matrix/federation/v1/state_ids/:roomId', ({ params, query }) => - eventService.getStateIds( + federationSDK.getStateIds( params.roomId as RoomID, query.event_id as EventID, ), @@ -46,7 +39,7 @@ export const statePlugin = (app: Elysia) => { .get( '/_matrix/federation/v1/state/:roomId', ({ params, query }) => - eventService.getState( + federationSDK.getState( params.roomId as RoomID, query.event_id as EventID, ), diff --git a/packages/homeserver/src/controllers/federation/transactions.controller.ts b/packages/homeserver/src/controllers/federation/transactions.controller.ts index 3dbc10cf4..d1928bbc6 100644 --- a/packages/homeserver/src/controllers/federation/transactions.controller.ts +++ b/packages/homeserver/src/controllers/federation/transactions.controller.ts @@ -1,13 +1,8 @@ import { EventID, RoomID } from '@rocket.chat/federation-room'; -import { - ConfigService, - EventAuthorizationService, - EventService, -} from '@rocket.chat/federation-sdk'; +import { federationSDK } from '@rocket.chat/federation-sdk'; import { canAccessResourceMiddleware } from '@rocket.chat/homeserver/middlewares/canAccessResource'; import { isAuthenticatedMiddleware } from '@rocket.chat/homeserver/middlewares/isAuthenticated'; import { Elysia } from 'elysia'; -import { container } from 'tsyringe'; import { BackfillErrorResponseDto, BackfillParamsDto, @@ -22,17 +17,13 @@ import { } from '../../dtos'; export const transactionsPlugin = (app: Elysia) => { - const eventService = container.resolve(EventService); - const configService = container.resolve(ConfigService); - const eventAuthService = container.resolve(EventAuthorizationService); - return app .put( '/_matrix/federation/v1/send/:txnId', async ({ body }) => { // TODO need to validate better the payload // biome-ignore lint/suspicious/noExplicitAny: - await eventService.processIncomingTransaction(body as any); + await federationSDK.processIncomingTransaction(body as any); return { pdus: {}, @@ -40,7 +31,7 @@ export const transactionsPlugin = (app: Elysia) => { }; }, { - use: isAuthenticatedMiddleware(eventAuthService), + use: isAuthenticatedMiddleware(), body: SendTransactionBodyDto, response: { 200: SendTransactionResponseDto, @@ -57,7 +48,9 @@ export const transactionsPlugin = (app: Elysia) => { .get( '/_matrix/federation/v1/event/:eventId', async ({ params, set }) => { - const eventData = await eventService.getEventById( + const serverName = federationSDK.getConfig('serverName'); + + const eventData = await federationSDK.getEventById( params.eventId as EventID, ); if (!eventData) { @@ -70,12 +63,12 @@ export const transactionsPlugin = (app: Elysia) => { return { origin_server_ts: eventData.event.origin_server_ts, - origin: configService.serverName, - pdus: [{ ...eventData.event, origin: configService.serverName }], + origin: serverName, + pdus: [{ ...eventData.event, origin: serverName }], }; }, { - use: canAccessResourceMiddleware(eventAuthService, 'event'), + use: canAccessResourceMiddleware('event'), params: GetEventParamsDto, response: { 200: GetEventResponseDto, @@ -110,7 +103,7 @@ export const transactionsPlugin = (app: Elysia) => { ? eventIdParam : [eventIdParam]; - return eventService.getBackfillEvents( + return federationSDK.getBackfillEvents( params.roomId as RoomID, eventIds as EventID[], limit, @@ -124,7 +117,7 @@ export const transactionsPlugin = (app: Elysia) => { } }, { - use: canAccessResourceMiddleware(eventAuthService, 'room'), + use: canAccessResourceMiddleware('room'), params: BackfillParamsDto, query: BackfillQueryDto, response: { diff --git a/packages/homeserver/src/controllers/federation/versions.controller.ts b/packages/homeserver/src/controllers/federation/versions.controller.ts index 914410ec3..acd34d28b 100644 --- a/packages/homeserver/src/controllers/federation/versions.controller.ts +++ b/packages/homeserver/src/controllers/federation/versions.controller.ts @@ -1,17 +1,18 @@ -import { ConfigService } from '@rocket.chat/federation-sdk'; +import { federationSDK } from '@rocket.chat/federation-sdk'; import { Elysia } from 'elysia'; -import { container } from 'tsyringe'; import { GetVersionsResponseDto } from '../../dtos'; export const versionsPlugin = (app: Elysia) => { - const configService = container.resolve(ConfigService); + const serverName = federationSDK.getConfig('serverName'); + const version = federationSDK.getConfig('version'); + return app.get( '/_matrix/federation/v1/version', () => { return { server: { - name: configService.serverName, - version: configService.version, + name: serverName, + version: version, }, }; }, diff --git a/packages/homeserver/src/controllers/internal/direct-message.controller.ts b/packages/homeserver/src/controllers/internal/direct-message.controller.ts index 736512a57..89145c0bb 100644 --- a/packages/homeserver/src/controllers/internal/direct-message.controller.ts +++ b/packages/homeserver/src/controllers/internal/direct-message.controller.ts @@ -1,5 +1,5 @@ import { UserID } from '@rocket.chat/federation-room'; -import { RoomService } from '@rocket.chat/federation-sdk'; +import { federationSDK } from '@rocket.chat/federation-sdk'; import { Elysia, t } from 'elysia'; import { container } from 'tsyringe'; import { type ErrorResponse, ErrorResponseDto } from '../../dtos'; @@ -20,8 +20,6 @@ export type InternalDirectMessageResponse = typeof InternalDirectMessageResponseDto.static; export const internalDirectMessagePlugin = (app: Elysia) => { - const roomService = container.resolve(RoomService); - return app.post( '/internal/direct-messages/create', async ({ @@ -30,7 +28,7 @@ export const internalDirectMessagePlugin = (app: Elysia) => { }): Promise => { const { senderUserId, targetUserId } = body; try { - const roomId = await roomService.createDirectMessageRoom( + const roomId = await federationSDK.createDirectMessageRoom( senderUserId as UserID, targetUserId as UserID, ); diff --git a/packages/homeserver/src/controllers/internal/external-federation-request.controller.ts b/packages/homeserver/src/controllers/internal/external-federation-request.controller.ts index a05cdae58..20c0cef78 100644 --- a/packages/homeserver/src/controllers/internal/external-federation-request.controller.ts +++ b/packages/homeserver/src/controllers/internal/external-federation-request.controller.ts @@ -7,24 +7,15 @@ import { type RoomVersion, type UserID, } from '@rocket.chat/federation-room'; -import { - FederationRequestService, - FederationService, - StateService, -} from '@rocket.chat/federation-sdk'; +import { federationSDK } from '@rocket.chat/federation-sdk'; import { Elysia, t } from 'elysia'; -import { container } from 'tsyringe'; export const internalRequestPlugin = (app: Elysia) => { - const requester = container.resolve(FederationRequestService); - const state = container.resolve(StateService); - const federation = container.resolve(FederationService); - app.post( '/internal/request', async ({ body }) => { const { method, body: requestBody, uri, serverName, query } = body; - const response = await requester.makeSignedRequest({ + const response = await federationSDK.makeSignedRequest({ domain: serverName, uri, method, @@ -77,7 +68,7 @@ export const internalRequestPlugin = (app: Elysia) => { PersistentEventFactory.defaultRoomVersion; switch (eventType) { case 'm.room.member': { - const event = await state.buildEvent<'m.room.member'>( + const event = await federationSDK.buildEvent<'m.room.member'>( { type: 'm.room.member', room_id: roomId, @@ -94,7 +85,7 @@ export const internalRequestPlugin = (app: Elysia) => { return event.event; } case 'm.room.message': { - const event = await state.buildEvent<'m.room.message'>( + const event = await federationSDK.buildEvent<'m.room.message'>( { type: 'm.room.message', room_id: roomId, @@ -119,14 +110,14 @@ export const internalRequestPlugin = (app: Elysia) => { state_default: 50, }; try { - const currState = await state.getLatestRoomState2(roomId); + const currState = await federationSDK.getLatestRoomState2(roomId); if (currState.powerLevels) { content = currState.powerLevels; } } catch { // noop } - const event = await state.buildEvent<'m.room.power_levels'>( + const event = await federationSDK.buildEvent<'m.room.power_levels'>( { type: 'm.room.power_levels', room_id: roomId, @@ -143,7 +134,7 @@ export const internalRequestPlugin = (app: Elysia) => { return event.event; } case 'm.room.join_rules': { - const event = await state.buildEvent<'m.room.join_rules'>( + const event = await federationSDK.buildEvent<'m.room.join_rules'>( { type: 'm.room.join_rules', room_id: roomId, @@ -160,7 +151,7 @@ export const internalRequestPlugin = (app: Elysia) => { return event.event; } case 'm.room.topic': { - const event = await state.buildEvent<'m.room.topic'>( + const event = await federationSDK.buildEvent<'m.room.topic'>( { type: 'm.room.topic', room_id: roomId, @@ -177,7 +168,7 @@ export const internalRequestPlugin = (app: Elysia) => { return event.event; } case 'm.room.name': { - const event = await state.buildEvent<'m.room.name'>( + const event = await federationSDK.buildEvent<'m.room.name'>( { type: 'm.room.name', room_id: roomId, @@ -229,8 +220,8 @@ export const internalRequestPlugin = (app: Elysia) => { if (!pdu) { throw new Error('Failed to create persistent event from event'); } - await state.handlePdu(pdu); - void federation.sendEventToAllServersInRoom(pdu); + await federationSDK.handlePdu(pdu); + void federationSDK.sendEventToAllServersInRoom(pdu); return { event_id: pdu.eventId, event: pdu.event }; }, { diff --git a/packages/homeserver/src/controllers/internal/invite.controller.ts b/packages/homeserver/src/controllers/internal/invite.controller.ts index eec1d1b90..197f25e13 100644 --- a/packages/homeserver/src/controllers/internal/invite.controller.ts +++ b/packages/homeserver/src/controllers/internal/invite.controller.ts @@ -3,9 +3,8 @@ import { RoomID, UserID, } from '@rocket.chat/federation-room'; -import { InviteService, StateService } from '@rocket.chat/federation-sdk'; +import { federationSDK } from '@rocket.chat/federation-sdk'; import { Elysia } from 'elysia'; -import { container } from 'tsyringe'; import { type ErrorResponse, ErrorResponseDto } from '../../dtos'; import { InternalInviteUserBodyDto, @@ -14,8 +13,6 @@ import { } from '../../dtos'; export const internalInvitePlugin = (app: Elysia) => { - const _inviteService = container.resolve(InviteService); - const stateService = container.resolve(StateService); return app.post( '/internal/invites', async ({ body }): Promise => { @@ -30,7 +27,7 @@ export const internalInvitePlugin = (app: Elysia) => { // } const { roomId, username, sender } = body; - const room = await stateService.getLatestRoomState(roomId); + const room = await federationSDK.getLatestRoomState(roomId); const createEvent = room.get('m.room.create:'); @@ -62,7 +59,7 @@ export const internalInvitePlugin = (app: Elysia) => { } } - await stateService.handlePdu(membershipEvent); + await federationSDK.handlePdu(membershipEvent); if (membershipEvent.rejected) { throw new Error(membershipEvent.rejectReason); diff --git a/packages/homeserver/src/controllers/internal/message.controller.ts b/packages/homeserver/src/controllers/internal/message.controller.ts index 862a64c8e..ce88c8aec 100644 --- a/packages/homeserver/src/controllers/internal/message.controller.ts +++ b/packages/homeserver/src/controllers/internal/message.controller.ts @@ -1,7 +1,6 @@ import { EventID, RoomID, UserID } from '@rocket.chat/federation-room'; -import { MessageService } from '@rocket.chat/federation-sdk'; +import { federationSDK } from '@rocket.chat/federation-sdk'; import { Elysia } from 'elysia'; -import { container } from 'tsyringe'; import { type ErrorResponse, ErrorResponseDto } from '../../dtos'; import { type InternalMessageResponse, @@ -20,7 +19,6 @@ import { } from '../../dtos'; export const internalMessagePlugin = (app: Elysia) => { - const messageService = container.resolve(MessageService); return app .patch( '/internal/messages/:messageId', @@ -31,7 +29,7 @@ export const internalMessagePlugin = (app: Elysia) => { }): Promise => { const { roomId, message, senderUserId } = body; try { - const eventId = await messageService.updateMessage( + const eventId = await federationSDK.updateMessage( roomId as RoomID, message, message, @@ -73,7 +71,7 @@ export const internalMessagePlugin = (app: Elysia) => { }): Promise => { const { roomId, emoji, senderUserId } = body; try { - const eventId = await messageService.sendReaction( + const eventId = await federationSDK.sendReaction( roomId as RoomID, params.messageId as EventID, emoji, @@ -114,7 +112,7 @@ export const internalMessagePlugin = (app: Elysia) => { }): Promise => { const { roomId, emoji, senderUserId } = body; try { - const eventId = await messageService.unsetReaction( + const eventId = await federationSDK.unsetReaction( roomId as RoomID, params.messageId as EventID, emoji, @@ -153,7 +151,7 @@ export const internalMessagePlugin = (app: Elysia) => { body, }): Promise => { const { roomId } = body; - const eventId = await messageService.redactMessage( + const eventId = await federationSDK.redactMessage( roomId as RoomID, params.messageId as EventID, ); diff --git a/packages/homeserver/src/controllers/internal/room.controller.ts b/packages/homeserver/src/controllers/internal/room.controller.ts index 1a4f14d78..a91d93a86 100644 --- a/packages/homeserver/src/controllers/internal/room.controller.ts +++ b/packages/homeserver/src/controllers/internal/room.controller.ts @@ -5,13 +5,8 @@ import { RoomID, UserID, } from '@rocket.chat/federation-room'; -import { - InviteService, - RoomService, - StateService, -} from '@rocket.chat/federation-sdk'; +import { federationSDK } from '@rocket.chat/federation-sdk'; import { Elysia, t } from 'elysia'; -import { container } from 'tsyringe'; import { type ErrorResponse, ErrorResponseDto, @@ -22,7 +17,6 @@ import { InternalBanUserBodyDto, InternalBanUserParamsDto, type InternalBanUserResponse, - InternalCreateRoomBodyDto, type InternalCreateRoomResponse, InternalCreateRoomResponseDto, InternalKickUserBodyDto, @@ -45,15 +39,12 @@ import { } from '../../dtos'; export const internalRoomPlugin = (app: Elysia) => { - const roomService = container.resolve(RoomService); - const stateService = container.resolve(StateService); - const inviteService = container.resolve(InviteService); return app .post( '/internal/rooms/rooms', async ({ body }): Promise => { const { creator, join_rule, name } = body; - return roomService.createRoom(creator as UserID, name, join_rule); + return federationSDK.createRoom(creator as UserID, name, join_rule); }, { body: t.Object({ @@ -92,7 +83,7 @@ export const internalRoomPlugin = (app: Elysia) => { }; } const { name, senderUserId } = bodyParse.data; - return roomService.updateRoomName( + return federationSDK.updateRoomName( roomIdParse.data as RoomID, name, senderUserId as UserID, @@ -139,7 +130,7 @@ export const internalRoomPlugin = (app: Elysia) => { } const { senderUserId, powerLevel } = bodyParse.data; try { - const eventId = await roomService.updateUserPowerLevel( + const eventId = await federationSDK.updateUserPowerLevel( params.roomId as RoomID, params.userId as UserID, powerLevel, @@ -173,7 +164,7 @@ export const internalRoomPlugin = (app: Elysia) => { async ({ params, query }) => { const eventId = query.event_id; if (eventId) { - const room = await stateService.findStateAtEvent(eventId as EventID); + const room = await federationSDK.findStateAtEvent(eventId as EventID); const state: Record = {}; for (const [key, value] of room.entries()) { state[key] = value.event; @@ -182,7 +173,7 @@ export const internalRoomPlugin = (app: Elysia) => { ...state, }; } - const room = await stateService.getLatestRoomState(params.roomId); + const room = await federationSDK.getLatestRoomState(params.roomId); const state: Record = {}; for (const [key, value] of room.entries()) { state[key] = value.event; @@ -220,7 +211,7 @@ export const internalRoomPlugin = (app: Elysia) => { } const { senderUserId } = bodyParse.data; try { - const eventId = await roomService.leaveRoom( + const eventId = await federationSDK.leaveRoom( roomIdParse.data as RoomID, senderUserId as UserID, ); @@ -274,7 +265,7 @@ export const internalRoomPlugin = (app: Elysia) => { } const { /*userIdToKick, */ senderUserId, reason } = bodyParse.data; try { - const eventId = await roomService.kickUser( + const eventId = await federationSDK.kickUser( params.roomId as RoomID, params.memberId as UserID, senderUserId as UserID, @@ -349,7 +340,7 @@ export const internalRoomPlugin = (app: Elysia) => { const { roomId, userIdToBan } = params; const { senderUserId } = body; - const room = await stateService.getLatestRoomState(roomId); + const room = await federationSDK.getLatestRoomState(roomId); const createEvent = room.get('m.room.create:'); @@ -382,7 +373,7 @@ export const internalRoomPlugin = (app: Elysia) => { } } - await stateService.handlePdu(membershipEvent); + await federationSDK.handlePdu(membershipEvent); return { eventId: membershipEvent.eventId, @@ -421,7 +412,7 @@ export const internalRoomPlugin = (app: Elysia) => { }, }; } - return roomService.markRoomAsTombstone( + return federationSDK.markRoomAsTombstone( roomIdParse.data as RoomID, bodyParse.data.sender as UserID, bodyParse.data.reason, @@ -443,13 +434,13 @@ export const internalRoomPlugin = (app: Elysia) => { }, ) .get('/internal/rooms/all', async () => { - const roomIds = await stateService.getAllRoomIds(); + const roomIds = await federationSDK.getAllRoomIds(); return { roomIds, }; }) .get('/internal/rooms/all/public', async () => { - const publicRooms = await stateService.getAllPublicRoomIdsAndNames(); + const publicRooms = await federationSDK.getAllPublicRoomIdsAndNames(); return { publicRooms, }; @@ -460,7 +451,7 @@ export const internalRoomPlugin = (app: Elysia) => { const { roomId, userId } = params; const { sender } = body; - const resp = await inviteService.inviteUserToRoom( + const resp = await federationSDK.inviteUserToRoom( userId as UserID, roomId as RoomID, sender as UserID, diff --git a/packages/homeserver/src/controllers/key/server.controller.ts b/packages/homeserver/src/controllers/key/server.controller.ts index 9088d9210..b1877620b 100644 --- a/packages/homeserver/src/controllers/key/server.controller.ts +++ b/packages/homeserver/src/controllers/key/server.controller.ts @@ -1,14 +1,12 @@ -import { ServerService } from '@rocket.chat/federation-sdk'; +import { federationSDK } from '@rocket.chat/federation-sdk'; import type { Elysia } from 'elysia'; -import { container } from 'tsyringe'; import { ServerKeyResponseDto } from '../../dtos'; export const serverKeyPlugin = (app: Elysia) => { - const serverService = container.resolve(ServerService); return app.get( '/_matrix/key/v2/server', async () => { - return serverService.getSignedServerKey(); + return federationSDK.getSignedServerKey(); }, { response: { diff --git a/packages/homeserver/src/controllers/well-known/well-known.controller.ts b/packages/homeserver/src/controllers/well-known/well-known.controller.ts index 5d5452c2e..e9e229e68 100644 --- a/packages/homeserver/src/controllers/well-known/well-known.controller.ts +++ b/packages/homeserver/src/controllers/well-known/well-known.controller.ts @@ -1,14 +1,12 @@ -import { WellKnownService } from '@rocket.chat/federation-sdk'; +import { federationSDK } from '@rocket.chat/federation-sdk'; import { Elysia } from 'elysia'; -import { container } from 'tsyringe'; import { WellKnownServerResponseDto } from '../../dtos'; export const wellKnownPlugin = (app: Elysia) => { - const wellKnownService = container.resolve(WellKnownService); return app.get( '/.well-known/matrix/server', ({ set }) => { - const responseData = wellKnownService.getWellKnownHostData(); + const responseData = federationSDK.getWellKnownHostData(); const etag = new Bun.CryptoHasher('md5') .update(JSON.stringify(responseData)) .digest('hex'); diff --git a/packages/homeserver/src/homeserver.module.ts b/packages/homeserver/src/homeserver.module.ts index c5665ef8b..23c18164d 100644 --- a/packages/homeserver/src/homeserver.module.ts +++ b/packages/homeserver/src/homeserver.module.ts @@ -5,10 +5,9 @@ import * as fs from 'node:fs'; import * as path from 'node:path'; import { - ConfigService, - type FederationContainerOptions, type HomeserverEventSignatures, - createFederationContainer, + federationSDK, + init, } from '@rocket.chat/federation-sdk'; import * as dotenv from 'dotenv'; @@ -32,27 +31,24 @@ import { internalRoomPlugin } from './controllers/internal/room.controller'; import { serverKeyPlugin } from './controllers/key/server.controller'; import { wellKnownPlugin } from './controllers/well-known/well-known.controller'; -export type { HomeserverEventSignatures }; -export interface HomeserverSetupOptions { - emitter?: Emitter; - containerOptions?: FederationContainerOptions; -} - -export async function setup(options?: HomeserverSetupOptions) { +export async function setup() { const envPath = path.resolve(process.cwd(), '.env'); if (fs.existsSync(envPath)) { dotenv.config({ path: envPath }); } - const config = new ConfigService({ - instanceId: crypto.randomUUID(), - serverName: process.env.SERVER_NAME || 'rc1', - port: Number.parseInt(process.env.SERVER_PORT || '8080', 10), - database: { + await init({ + dbConfig: { uri: process.env.MONGODB_URI || 'mongodb://localhost:27017/matrix', name: process.env.DATABASE_NAME || 'matrix', poolSize: Number.parseInt(process.env.DATABASE_POOL_SIZE || '10', 10), }, + }); + + federationSDK.setConfig({ + instanceId: crypto.randomUUID(), + serverName: process.env.SERVER_NAME || 'rc1', + port: Number.parseInt(process.env.SERVER_PORT || '8080', 10), matrixDomain: process.env.MATRIX_DOMAIN || 'rc1', keyRefreshInterval: Number.parseInt( process.env.MATRIX_KEY_REFRESH_INTERVAL || '60', @@ -96,12 +92,6 @@ export async function setup(options?: HomeserverSetupOptions) { }, }); - const containerOptions: FederationContainerOptions = { - emitter: options?.emitter, - }; - - const container = await createFederationContainer(containerOptions, config); - const app = new Elysia(); app @@ -135,7 +125,7 @@ export async function setup(options?: HomeserverSetupOptions) { .use(mediaPlugin) .use(internalRequestPlugin); - return { app, container }; + return { app }; } export const appPromise = setup().then(({ app }) => app); diff --git a/packages/homeserver/src/middlewares/canAccessResource.ts b/packages/homeserver/src/middlewares/canAccessResource.ts index 38485e1e5..ccdd9ffe3 100644 --- a/packages/homeserver/src/middlewares/canAccessResource.ts +++ b/packages/homeserver/src/middlewares/canAccessResource.ts @@ -1,4 +1,4 @@ -import type { EventAuthorizationService } from '@rocket.chat/federation-sdk'; +import { federationSDK } from '@rocket.chat/federation-sdk'; import { errCodes } from '@rocket.chat/federation-sdk'; import Elysia from 'elysia'; import { isAuthenticatedMiddleware } from './isAuthenticated'; @@ -23,11 +23,10 @@ function extractEntityId( } export const canAccessResourceMiddleware = ( - federationAuth: EventAuthorizationService, entityType: 'event' | 'media' | 'room', ) => { return new Elysia({ name: 'homeserver/canAccessResource' }) - .use(isAuthenticatedMiddleware(federationAuth)) + .use(isAuthenticatedMiddleware()) .onBeforeHandle(async ({ params, authenticatedServer, set }) => { try { if (!authenticatedServer) { @@ -47,7 +46,7 @@ export const canAccessResourceMiddleware = ( }; } - const resourceAccess = await federationAuth.canAccessResource( + const resourceAccess = await federationSDK.canAccessResource( entityType, resourceId, authenticatedServer, diff --git a/packages/homeserver/src/middlewares/isAuthenticated.ts b/packages/homeserver/src/middlewares/isAuthenticated.ts index 9588c0174..125912618 100644 --- a/packages/homeserver/src/middlewares/isAuthenticated.ts +++ b/packages/homeserver/src/middlewares/isAuthenticated.ts @@ -1,9 +1,7 @@ -import type { EventAuthorizationService } from '@rocket.chat/federation-sdk'; +import { federationSDK } from '@rocket.chat/federation-sdk'; import Elysia from 'elysia'; -export const isAuthenticatedMiddleware = ( - federationAuth: EventAuthorizationService, -) => { +export const isAuthenticatedMiddleware = () => { return new Elysia({ name: 'homeserver/isAuthenticated', }) @@ -32,7 +30,7 @@ export const isAuthenticatedMiddleware = ( } } - const isValid = await federationAuth.verifyRequestSignature( + const isValid = await federationSDK.verifyRequestSignature( authorizationHeader, method, uri,