From 2e9969d5906f22c5013ed1bf4713952d40111897 Mon Sep 17 00:00:00 2001 From: Philipinho <16838612+Philipinho@users.noreply.github.com> Date: Mon, 16 Oct 2023 15:19:30 +0100 Subject: [PATCH] socket.io server init * Make Hocuspocus collaboration websocket server (wsAdapter) work with socket.io * Create database module for TypeOrm --- .../page/tree/hooks/use-persistence.ts | 2 +- server/package.json | 4 ++- server/src/app.module.ts | 12 +++---- .../collaboration/collaboration.gateway.ts | 19 +++++----- .../src/collaboration/collaboration.module.ts | 36 +++++++++++++++++-- server/src/database/database.module.ts | 15 ++++++++ server/src/main.ts | 3 +- server/src/ws/ws.gateway.ts | 27 ++++++++++++++ server/src/ws/ws.module.ts | 7 ++++ 9 files changed, 99 insertions(+), 26 deletions(-) create mode 100644 server/src/database/database.module.ts create mode 100644 server/src/ws/ws.gateway.ts create mode 100644 server/src/ws/ws.module.ts diff --git a/frontend/src/features/page/tree/hooks/use-persistence.ts b/frontend/src/features/page/tree/hooks/use-persistence.ts index 60db53d..ad989be 100644 --- a/frontend/src/features/page/tree/hooks/use-persistence.ts +++ b/frontend/src/features/page/tree/hooks/use-persistence.ts @@ -29,7 +29,7 @@ export function usePersistence() { setData(tree.data); const currentTreeData = args.parentId ? tree.find(args.parentId).children : tree.data; - const afterId = currentTreeData[args.index - 2]?.id || null; + const afterId = currentTreeData[args.index - 2]?.id || null; //TODO: fix after Id bug const beforeId = !afterId && currentTreeData[args.index + 1]?.id || null; const params: IMovePage= { diff --git a/server/package.json b/server/package.json index 2bcc8ab..d7f032f 100644 --- a/server/package.json +++ b/server/package.json @@ -37,9 +37,10 @@ "@nestjs/jwt": "^10.1.0", "@nestjs/mapped-types": "^2.0.2", "@nestjs/platform-fastify": "^10.2.4", + "@nestjs/platform-socket.io": "^10.2.7", "@nestjs/platform-ws": "^10.2.4", "@nestjs/typeorm": "^10.0.0", - "@nestjs/websockets": "^10.2.4", + "@nestjs/websockets": "^10.2.7", "bcrypt": "^5.1.1", "class-transformer": "^0.5.1", "class-validator": "^0.14.0", @@ -49,6 +50,7 @@ "pg": "^8.11.3", "reflect-metadata": "^0.1.13", "rxjs": "^7.8.1", + "socket.io": "^4.7.2", "typeorm": "^0.3.17", "uuid": "^9.0.0", "ws": "^8.14.1" diff --git a/server/src/app.module.ts b/server/src/app.module.ts index 6918ce2..4146677 100644 --- a/server/src/app.module.ts +++ b/server/src/app.module.ts @@ -3,21 +3,17 @@ import { AppController } from './app.controller'; import { AppService } from './app.service'; import { CoreModule } from './core/core.module'; import { EnvironmentModule } from './environment/environment.module'; -import { TypeOrmModule } from '@nestjs/typeorm'; -import { AppDataSource } from './database/typeorm.config'; import { CollaborationModule } from './collaboration/collaboration.module'; +import { DatabaseModule } from './database/database.module'; +import { WsModule } from './ws/ws.module'; @Module({ imports: [ CoreModule, EnvironmentModule, - TypeOrmModule.forRoot({ - ...AppDataSource.options, - entities: ['dist/src/**/*.entity.{ts,js}'], - migrations: ['dist/src/**/migrations/*.{ts,js}'], - autoLoadEntities: true, - }), + DatabaseModule, CollaborationModule, + WsModule, ], controllers: [AppController], providers: [AppService], diff --git a/server/src/collaboration/collaboration.gateway.ts b/server/src/collaboration/collaboration.gateway.ts index c4719d3..983d7d3 100644 --- a/server/src/collaboration/collaboration.gateway.ts +++ b/server/src/collaboration/collaboration.gateway.ts @@ -1,19 +1,12 @@ -import { - OnGatewayConnection, - WebSocketGateway, - WebSocketServer, -} from '@nestjs/websockets'; import { Server as HocuspocusServer } from '@hocuspocus/server'; import { IncomingMessage } from 'http'; -import WebSocket, { Server } from 'ws'; +import WebSocket from 'ws'; import { AuthenticationExtension } from './extensions/authentication.extension'; import { PersistenceExtension } from './extensions/persistence.extension'; +import { Injectable } from '@nestjs/common'; -@WebSocketGateway({ path: '/collaboration' }) -export class CollaborationGateway implements OnGatewayConnection { - @WebSocketServer() - server: Server; - +@Injectable() +export class CollaborationGateway { constructor( private authenticationExtension: AuthenticationExtension, private persistenceExtension: PersistenceExtension, @@ -28,4 +21,8 @@ export class CollaborationGateway implements OnGatewayConnection { handleConnection(client: WebSocket, request: IncomingMessage): any { this.hocuspocus.handleConnection(client, request); } + + handleDestroy() { + this.hocuspocus.destroy(); + } } diff --git a/server/src/collaboration/collaboration.module.ts b/server/src/collaboration/collaboration.module.ts index a286a61..025ecef 100644 --- a/server/src/collaboration/collaboration.module.ts +++ b/server/src/collaboration/collaboration.module.ts @@ -1,10 +1,14 @@ -import { Module } from '@nestjs/common'; +import { Module, OnModuleDestroy, OnModuleInit } from '@nestjs/common'; import { UserModule } from '../core/user/user.module'; import { AuthModule } from '../core/auth/auth.module'; -import { CollaborationGateway } from './collaboration.gateway'; import { AuthenticationExtension } from './extensions/authentication.extension'; import { PersistenceExtension } from './extensions/persistence.extension'; import { PageModule } from '../core/page/page.module'; +import { CollaborationGateway } from './collaboration.gateway'; +import { HttpAdapterHost } from '@nestjs/core'; +import { WsAdapter } from '@nestjs/platform-ws'; +import WebSocket from 'ws'; +import { IncomingMessage } from 'http'; @Module({ providers: [ @@ -14,4 +18,30 @@ import { PageModule } from '../core/page/page.module'; ], imports: [UserModule, AuthModule, PageModule], }) -export class CollaborationModule {} +export class CollaborationModule implements OnModuleInit, OnModuleDestroy { + constructor( + private readonly collaborationGateway: CollaborationGateway, + private readonly httpAdapterHost: HttpAdapterHost, + ) {} + + onModuleInit() { + const port = 0; // zero to reuse existing server port + const path = '/collaboration'; + + const httpServer = this.httpAdapterHost.httpAdapter.getHttpServer(); + const wsAdapter = new WsAdapter(httpServer).create(port, { + path, + }); + + wsAdapter.on( + 'connection', + (client: WebSocket, request: IncomingMessage) => { + this.collaborationGateway.handleConnection(client, request); + }, + ); + } + + onModuleDestroy(): any { + this.collaborationGateway.handleDestroy(); + } +} diff --git a/server/src/database/database.module.ts b/server/src/database/database.module.ts new file mode 100644 index 0000000..6551555 --- /dev/null +++ b/server/src/database/database.module.ts @@ -0,0 +1,15 @@ +import { Module } from '@nestjs/common'; +import { TypeOrmModule } from '@nestjs/typeorm'; +import { AppDataSource } from './typeorm.config'; + +@Module({ + imports: [ + TypeOrmModule.forRoot({ + ...AppDataSource.options, + entities: ['dist/src/**/*.entity.{ts,js}'], + migrations: ['dist/src/**/migrations/*.{ts,js}'], + autoLoadEntities: true, + }), + ], +}) +export class DatabaseModule {} diff --git a/server/src/main.ts b/server/src/main.ts index f0dae83..169f67b 100644 --- a/server/src/main.ts +++ b/server/src/main.ts @@ -6,7 +6,6 @@ import { } from '@nestjs/platform-fastify'; import { ValidationPipe } from '@nestjs/common'; import { TransformHttpResponseInterceptor } from './interceptors/http-response.interceptor'; -import { WsAdapter } from '@nestjs/platform-ws'; async function bootstrap() { const app = await NestFactory.create( @@ -26,7 +25,7 @@ async function bootstrap() { app.enableCors(); app.useGlobalInterceptors(new TransformHttpResponseInterceptor()); - app.useWebSocketAdapter(new WsAdapter(app)); + app.enableShutdownHooks(); await app.listen(process.env.PORT || 3001); } diff --git a/server/src/ws/ws.gateway.ts b/server/src/ws/ws.gateway.ts new file mode 100644 index 0000000..f8391a8 --- /dev/null +++ b/server/src/ws/ws.gateway.ts @@ -0,0 +1,27 @@ +import { + OnGatewayConnection, + OnGatewayInit, + SubscribeMessage, + WebSocketGateway, + WebSocketServer, +} from '@nestjs/websockets'; +import { Server } from 'socket.io'; + +@WebSocketGateway({ namespace: 'events' }) +export class WsGateway implements OnGatewayInit, OnGatewayConnection { + @WebSocketServer() + server: Server; + + @SubscribeMessage('message') + handleMessage(client: any, payload: any): string { + return 'Hello world!'; + } + + handleConnection(client: any, ...args: any[]): any { + // + } + + afterInit(server: any): any { + // + } +} diff --git a/server/src/ws/ws.module.ts b/server/src/ws/ws.module.ts new file mode 100644 index 0000000..058d3df --- /dev/null +++ b/server/src/ws/ws.module.ts @@ -0,0 +1,7 @@ +import { Module } from '@nestjs/common'; +import { WsGateway } from './ws.gateway'; + +@Module({ + providers: [WsGateway], +}) +export class WsModule {}