From a2768e7d3053dac99dd62b730ddb9852e1f63493 Mon Sep 17 00:00:00 2001 From: Philipinho <16838612+Philipinho@users.noreply.github.com> Date: Sat, 4 May 2024 17:21:44 +0100 Subject: [PATCH] collaboration module cleanup x2 --- .../adapter/collab-ws.adapter.ts | 4 ++-- .../src/collaboration/collaboration.module.ts | 2 +- .../src/collaboration/collaboration.util.ts | 4 ++++ .../extensions/authentication.extension.ts | 20 +++++++++++++++--- .../extensions/history.extension.ts | 17 ++++++++++----- .../extensions/persistence.extension.ts | 21 ++++++++++--------- 6 files changed, 47 insertions(+), 21 deletions(-) diff --git a/apps/server/src/collaboration/adapter/collab-ws.adapter.ts b/apps/server/src/collaboration/adapter/collab-ws.adapter.ts index 9d84d8f9..352fe01f 100644 --- a/apps/server/src/collaboration/adapter/collab-ws.adapter.ts +++ b/apps/server/src/collaboration/adapter/collab-ws.adapter.ts @@ -7,8 +7,8 @@ export class CollabWsAdapter { this.wss = new WebSocketServer({ noServer: true }); } - handleUpgrade(path: string, httpServer) { - httpServer.on('upgrade', (request, socket, head) => { + handleUpgrade(path: string, httpServer: any) { + httpServer.on('upgrade', (request: any, socket: any, head: any) => { try { const baseUrl = 'ws://' + request.headers.host + '/'; const pathname = new URL(request.url, baseUrl).pathname; diff --git a/apps/server/src/collaboration/collaboration.module.ts b/apps/server/src/collaboration/collaboration.module.ts index 54e5ff7f..28249436 100644 --- a/apps/server/src/collaboration/collaboration.module.ts +++ b/apps/server/src/collaboration/collaboration.module.ts @@ -20,7 +20,7 @@ import { HistoryExtension } from './extensions/history.extension'; }) export class CollaborationModule implements OnModuleInit, OnModuleDestroy { private collabWsAdapter: CollabWsAdapter; - private path = '/collaboration'; + private path = '/collab'; constructor( private readonly collaborationGateway: CollaborationGateway, diff --git a/apps/server/src/collaboration/collaboration.util.ts b/apps/server/src/collaboration/collaboration.util.ts index 6513e115..2487a442 100644 --- a/apps/server/src/collaboration/collaboration.util.ts +++ b/apps/server/src/collaboration/collaboration.util.ts @@ -42,3 +42,7 @@ export function htmlToJson(html: string) { export function jsonToText(tiptapJson: JSONContent) { return generateText(tiptapJson, tiptapExtensions); } + +export function getPageId(documentName: string) { + return documentName.split('.')[1]; +} diff --git a/apps/server/src/collaboration/extensions/authentication.extension.ts b/apps/server/src/collaboration/extensions/authentication.extension.ts index bbe06e8c..e515280d 100644 --- a/apps/server/src/collaboration/extensions/authentication.extension.ts +++ b/apps/server/src/collaboration/extensions/authentication.extension.ts @@ -1,14 +1,22 @@ import { Extension, onAuthenticatePayload } from '@hocuspocus/server'; -import { Injectable, UnauthorizedException } from '@nestjs/common'; +import { + Injectable, + Logger, + NotFoundException, + UnauthorizedException, +} from '@nestjs/common'; import { TokenService } from '../../core/auth/services/token.service'; import { UserRepo } from '@docmost/db/repos/user/user.repo'; import { PageRepo } from '@docmost/db/repos/page/page.repo'; import { SpaceMemberRepo } from '@docmost/db/repos/space/space-member.repo'; import { findHighestUserSpaceRole } from '@docmost/db/repos/space/utils'; import { SpaceRole } from '../../helpers/types/permission'; +import { getPageId } from '../collaboration.util'; @Injectable() export class AuthenticationExtension implements Extension { + private readonly logger = new Logger(AuthenticationExtension.name); + constructor( private tokenService: TokenService, private userRepo: UserRepo, @@ -18,6 +26,7 @@ export class AuthenticationExtension implements Extension { async onAuthenticate(data: onAuthenticatePayload) { const { documentName, token } = data; + const pageId = getPageId(documentName); let jwtPayload = null; @@ -36,9 +45,10 @@ export class AuthenticationExtension implements Extension { throw new UnauthorizedException(); } - const page = await this.pageRepo.findById(documentName); + const page = await this.pageRepo.findById(pageId); if (!page) { - throw new UnauthorizedException('Page not found'); + this.logger.warn(`Page not found: ${pageId}}`); + throw new NotFoundException('Page not found'); } const userSpaceRoles = await this.spaceMemberRepo.getUserSpaceRoles( @@ -49,13 +59,17 @@ export class AuthenticationExtension implements Extension { const userSpaceRole = findHighestUserSpaceRole(userSpaceRoles); if (!userSpaceRole) { + this.logger.warn(`User authorized to access page: ${pageId}}`); throw new UnauthorizedException(); } if (userSpaceRole === SpaceRole.READER) { data.connection.readOnly = true; + this.logger.warn(`User granted readonly access to page: ${pageId}}`); } + this.logger.debug(`Authenticated user ${user.id} on page ${pageId}`); + return { user, }; diff --git a/apps/server/src/collaboration/extensions/history.extension.ts b/apps/server/src/collaboration/extensions/history.extension.ts index 28c3879d..1d675bb7 100644 --- a/apps/server/src/collaboration/extensions/history.extension.ts +++ b/apps/server/src/collaboration/extensions/history.extension.ts @@ -3,12 +3,15 @@ import { onChangePayload, onDisconnectPayload, } from '@hocuspocus/server'; -import { Injectable } from '@nestjs/common'; +import { Injectable, Logger } from '@nestjs/common'; import { PageRepo } from '@docmost/db/repos/page/page.repo'; import { PageHistoryRepo } from '@docmost/db/repos/page/page-history.repo'; +import { getPageId } from '../collaboration.util'; @Injectable() export class HistoryExtension implements Extension { + private readonly logger = new Logger(HistoryExtension.name); + ACTIVE_EDITING_INTERVAL = 10 * 60 * 1000; // 10 minutes historyIntervalMap = new Map(); lastEditTimeMap = new Map(); @@ -19,7 +22,8 @@ export class HistoryExtension implements Extension { ) {} async onChange(data: onChangePayload): Promise { - const pageId = data.documentName; + const pageId = getPageId(data.documentName); + this.lastEditTimeMap.set(pageId, Date.now()); if (!this.historyIntervalMap.has(pageId)) { @@ -33,7 +37,7 @@ export class HistoryExtension implements Extension { } async onDisconnect(data: onDisconnectPayload): Promise { - const pageId = data.documentName; + const pageId = getPageId(data.documentName); if (data.clientsCount === 0) { if (this.historyIntervalMap.has(pageId)) { clearInterval(this.historyIntervalMap.get(pageId)); @@ -58,9 +62,12 @@ export class HistoryExtension implements Extension { }); // Todo: compare if data is the same as the previous version await this.pageHistoryRepo.saveHistory(page); - console.log(`New history created for: ${pageId}`); + this.logger.debug(`New history created for: ${pageId}`); } catch (err) { - console.error('An error occurred saving page history', err); + this.logger.error( + `An error occurred saving page history for: ${pageId}`, + err, + ); } } } diff --git a/apps/server/src/collaboration/extensions/persistence.extension.ts b/apps/server/src/collaboration/extensions/persistence.extension.ts index 2fa234e1..760a9485 100644 --- a/apps/server/src/collaboration/extensions/persistence.extension.ts +++ b/apps/server/src/collaboration/extensions/persistence.extension.ts @@ -4,18 +4,20 @@ import { onStoreDocumentPayload, } from '@hocuspocus/server'; import * as Y from 'yjs'; -import { Injectable } from '@nestjs/common'; +import { Injectable, Logger } from '@nestjs/common'; import { TiptapTransformer } from '@hocuspocus/transformer'; -import { jsonToText, tiptapExtensions } from '../collaboration.util'; +import { getPageId, jsonToText, tiptapExtensions } from '../collaboration.util'; import { PageRepo } from '@docmost/db/repos/page/page.repo'; @Injectable() export class PersistenceExtension implements Extension { + private readonly logger = new Logger(PersistenceExtension.name); + constructor(private readonly pageRepo: PageRepo) {} async onLoadDocument(data: onLoadDocumentPayload) { const { documentName, document } = data; - const pageId = documentName; + const pageId = getPageId(documentName); if (!document.isEmpty('default')) { return; @@ -27,13 +29,12 @@ export class PersistenceExtension implements Extension { }); if (!page) { - console.log('page does not exist.'); - //TODO: terminate connection if the page does not exist? + this.logger.warn('page not found'); return; } if (page.ydoc) { - console.log('ydoc loaded from db'); + this.logger.debug(`ydoc loaded from db: ${pageId}`); const doc = new Y.Doc(); const dbState = new Uint8Array(page.ydoc); @@ -44,7 +45,7 @@ export class PersistenceExtension implements Extension { // if no ydoc state in db convert json in page.content to Ydoc. if (page.content) { - console.log('converting json to ydoc'); + this.logger.debug(`converting json to ydoc: ${pageId}`); const ydoc = TiptapTransformer.toYdoc( page.content, @@ -56,14 +57,14 @@ export class PersistenceExtension implements Extension { return ydoc; } - console.log('creating fresh ydoc'); + this.logger.debug(`creating fresh ydoc': ${pageId}`); return new Y.Doc(); } async onStoreDocument(data: onStoreDocumentPayload) { const { documentName, document, context } = data; - const pageId = documentName; + const pageId = getPageId(documentName); const tiptapJson = TiptapTransformer.fromYdoc(document, 'default'); const ydocState = Buffer.from(Y.encodeStateAsUpdate(document)); @@ -81,7 +82,7 @@ export class PersistenceExtension implements Extension { pageId, ); } catch (err) { - console.error(`Failed to update page ${documentName}`); + this.logger.error(`Failed to update page ${pageId}`, err); } } }