feat(guild-realtime): add websocket gateway with api-key auth and channel rooms

This commit is contained in:
nav
2026-05-12 12:13:19 +00:00
parent 5b28ad52bb
commit 01090273c6
6 changed files with 399 additions and 1 deletions

View File

@@ -8,11 +8,13 @@ import { GuildsModule } from './guilds/guilds.module';
import { ChannelsModule } from './channels/channels.module';
import { MessagingModule } from './messaging/messaging.module';
import { EventsModule } from './events/events.module';
import { RealtimeModule } from './realtime/realtime.module';
@Module({
imports: [
TypeOrmModule.forRoot(buildTypeOrmConfig()),
EventsModule,
RealtimeModule,
GuildsModule,
ChannelsModule,
MessagingModule,

View File

@@ -0,0 +1,72 @@
import {
ConnectedSocket,
MessageBody,
OnGatewayConnection,
OnGatewayDisconnect,
SubscribeMessage,
WebSocketGateway,
WebSocketServer,
} from '@nestjs/websockets';
import { Logger } from '@nestjs/common';
import { Server, Socket } from 'socket.io';
@WebSocketGateway({
namespace: '/realtime',
cors: {
origin: '*',
},
})
export class RealtimeGateway implements OnGatewayConnection, OnGatewayDisconnect {
@WebSocketServer()
server!: Server;
private readonly logger = new Logger(RealtimeGateway.name);
handleConnection(client: Socket): void {
const expected = process.env.FABRIC_API_KEY;
if (!expected) {
client.disconnect(true);
return;
}
const authKey = client.handshake.auth?.apiKey;
const headerKey = client.handshake.headers['x-api-key'];
const apiKey = typeof authKey === 'string' ? authKey : Array.isArray(headerKey) ? headerKey[0] : headerKey;
if (apiKey !== expected) {
this.logger.warn(`socket rejected: ${client.id}`);
client.disconnect(true);
return;
}
this.logger.log(`socket connected: ${client.id}`);
}
handleDisconnect(client: Socket): void {
this.logger.log(`socket disconnected: ${client.id}`);
}
@SubscribeMessage('join_channel')
joinChannel(
@ConnectedSocket() client: Socket,
@MessageBody() body: { channelId?: string },
): { ok: boolean } {
if (!body?.channelId) return { ok: false };
client.join(`channel:${body.channelId}`);
return { ok: true };
}
@SubscribeMessage('leave_channel')
leaveChannel(
@ConnectedSocket() client: Socket,
@MessageBody() body: { channelId?: string },
): { ok: boolean } {
if (!body?.channelId) return { ok: false };
client.leave(`channel:${body.channelId}`);
return { ok: true };
}
emitChannelEvent(channelId: string, event: string, data: Record<string, unknown>): void {
this.server.to(`channel:${channelId}`).emit(event, data);
}
}

View File

@@ -0,0 +1,9 @@
import { Global, Module } from '@nestjs/common';
import { RealtimeGateway } from './realtime.gateway';
@Global()
@Module({
providers: [RealtimeGateway],
exports: [RealtimeGateway],
})
export class RealtimeModule {}