feat: add realtime announcements for permission changes and note deletion

Co-authored-by: Erik Michelson <github@erik.michelson.eu>
Signed-off-by: Erik Michelson <github@erik.michelson.eu>
Signed-off-by: Philip Molares <philip.molares@udo.edu>
This commit is contained in:
Philip Molares 2022-09-04 23:38:26 +02:00
parent c363d0834e
commit 331747f61b
6 changed files with 56 additions and 3 deletions

View file

@ -260,6 +260,10 @@ export class NotesService {
* @throws {NotInDBError} there is no note with this id or alias
*/
async deleteNote(note: Note): Promise<Note> {
const realtimeNote = this.realtimeNoteStore.find(note.id);
if (realtimeNote) {
realtimeNote.announceNoteDeletion();
}
return await this.noteRepository.remove(note);
}

View file

@ -3,12 +3,13 @@
*
* SPDX-License-Identifier: AGPL-3.0-only
*/
import { Module } from '@nestjs/common';
import { forwardRef, Module } from '@nestjs/common';
import { TypeOrmModule } from '@nestjs/typeorm';
import { GroupsModule } from '../groups/groups.module';
import { LoggerModule } from '../logger/logger.module';
import { Note } from '../notes/note.entity';
import { RealtimeNoteModule } from '../realtime/realtime-note/realtime-note.module';
import { UsersModule } from '../users/users.module';
import { PermissionsService } from './permissions.service';
@ -18,6 +19,7 @@ import { PermissionsService } from './permissions.service';
UsersModule,
GroupsModule,
LoggerModule,
forwardRef(() => RealtimeNoteModule),
],
exports: [PermissionsService],
providers: [PermissionsService],

View file

@ -34,6 +34,7 @@ import {
import { Note } from '../notes/note.entity';
import { NotesModule } from '../notes/notes.module';
import { Tag } from '../notes/tag.entity';
import { RealtimeNoteModule } from '../realtime/realtime-note/realtime-note.module';
import { Edit } from '../revisions/edit.entity';
import { Revision } from '../revisions/revision.entity';
import { Session } from '../users/session.entity';
@ -109,6 +110,7 @@ describe('PermissionsService', () => {
],
}),
GroupsModule,
RealtimeNoteModule,
],
})
.overrideProvider(getRepositoryToken(User))

View file

@ -19,6 +19,8 @@ import { SpecialGroup } from '../groups/groups.special';
import { ConsoleLoggerService } from '../logger/console-logger.service';
import { NotePermissionsUpdateDto } from '../notes/note-permissions.dto';
import { Note } from '../notes/note.entity';
import { RealtimeNoteStore } from '../realtime/realtime-note/realtime-note-store';
import { RealtimeNoteService } from '../realtime/realtime-note/realtime-note.service';
import { User } from '../users/user.entity';
import { UsersService } from '../users/users.service';
import { checkArrayForDuplicates } from '../utils/arrayDuplicatCheck';
@ -34,6 +36,8 @@ export class PermissionsService {
private readonly logger: ConsoleLoggerService,
@Inject(noteConfiguration.KEY)
private noteConfig: NoteConfig,
private realtimeNoteService: RealtimeNoteService,
private realtimeNoteStore: RealtimeNoteStore,
) {}
/**
@ -150,6 +154,13 @@ export class PermissionsService {
return false;
}
private notifyOthers(noteId: Note['id']): void {
const realtimeNote = this.realtimeNoteStore.find(noteId);
if (realtimeNote) {
realtimeNote.announcePermissionChange();
}
}
/**
* @async
* Update a notes permissions.
@ -211,7 +222,7 @@ export class PermissionsService {
createdPermission.note = Promise.resolve(note);
(await note.groupPermissions).push(createdPermission);
}
this.notifyOthers(note.id);
return await this.noteRepository.save(note);
}
@ -245,6 +256,7 @@ export class PermissionsService {
);
(await note.userPermissions).push(noteUserPermission);
}
this.notifyOthers(note.id);
return await this.noteRepository.save(note);
}
@ -264,6 +276,7 @@ export class PermissionsService {
}
}
note.userPermissions = Promise.resolve(newPermissions);
this.notifyOthers(note.id);
return await this.noteRepository.save(note);
}
@ -305,6 +318,7 @@ export class PermissionsService {
);
(await note.groupPermissions).push(noteGroupPermission);
}
this.notifyOthers(note.id);
return await this.noteRepository.save(note);
}
@ -327,6 +341,7 @@ export class PermissionsService {
}
}
note.groupPermissions = Promise.resolve(newPermissions);
this.notifyOthers(note.id);
return await this.noteRepository.save(note);
}
@ -339,6 +354,7 @@ export class PermissionsService {
*/
async changeOwner(note: Note, owner: User): Promise<Note> {
note.owner = Promise.resolve(owner);
this.notifyOthers(note.id);
return await this.noteRepository.save(note);
}
}

View file

@ -47,7 +47,7 @@ export class RealtimeNoteService implements BeforeApplicationShutdown {
/**
* Creates or reuses a {@link RealtimeNote} that is handling the real time editing of the {@link Note} which is identified by the given note id.
* @param note The for which a {@link RealtimeNote realtime note} should be retrieved.
* @param note The {@link Note} for which a {@link RealtimeNote realtime note} should be retrieved.
* @throws NotInDBError if note doesn't exist or has no revisions.
* @return A {@link RealtimeNote} that is linked to the given note.
*/

View file

@ -3,6 +3,10 @@
*
* SPDX-License-Identifier: AGPL-3.0-only
*/
import {
encodeDocumentDeletedMessage,
encodeMetadataUpdatedMessage,
} from '@hedgedoc/realtime';
import { Logger } from '@nestjs/common';
import { EventEmitter } from 'events';
import TypedEventEmitter, { EventMap } from 'typed-emitter';
@ -130,4 +134,29 @@ export class RealtimeNote extends (EventEmitter as TypedEventEmitterConstructor<
public getNote(): Note {
return this.note;
}
/**
* Announce to all clients that the permissions of the note have been changed.
*/
public announcePermissionChange(): void {
this.sendToAllClients(encodeMetadataUpdatedMessage());
}
/**
* Announce to all clients that the note has been deleted.
*/
public announceNoteDeletion(): void {
this.sendToAllClients(encodeDocumentDeletedMessage());
}
/**
* Broadcasts the given content to all connected clients.
*
* @param {Uint8Array} content The binary message to broadcast
*/
private sendToAllClients(content: Uint8Array): void {
this.getConnections().forEach((connection) => {
connection.send(content);
});
}
}