mirror of
https://github.com/hedgedoc/hedgedoc.git
synced 2025-05-19 09:45:37 -04:00
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:
parent
c363d0834e
commit
331747f61b
6 changed files with 56 additions and 3 deletions
|
@ -260,6 +260,10 @@ export class NotesService {
|
||||||
* @throws {NotInDBError} there is no note with this id or alias
|
* @throws {NotInDBError} there is no note with this id or alias
|
||||||
*/
|
*/
|
||||||
async deleteNote(note: Note): Promise<Note> {
|
async deleteNote(note: Note): Promise<Note> {
|
||||||
|
const realtimeNote = this.realtimeNoteStore.find(note.id);
|
||||||
|
if (realtimeNote) {
|
||||||
|
realtimeNote.announceNoteDeletion();
|
||||||
|
}
|
||||||
return await this.noteRepository.remove(note);
|
return await this.noteRepository.remove(note);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -3,12 +3,13 @@
|
||||||
*
|
*
|
||||||
* SPDX-License-Identifier: AGPL-3.0-only
|
* SPDX-License-Identifier: AGPL-3.0-only
|
||||||
*/
|
*/
|
||||||
import { Module } from '@nestjs/common';
|
import { forwardRef, Module } from '@nestjs/common';
|
||||||
import { TypeOrmModule } from '@nestjs/typeorm';
|
import { TypeOrmModule } from '@nestjs/typeorm';
|
||||||
|
|
||||||
import { GroupsModule } from '../groups/groups.module';
|
import { GroupsModule } from '../groups/groups.module';
|
||||||
import { LoggerModule } from '../logger/logger.module';
|
import { LoggerModule } from '../logger/logger.module';
|
||||||
import { Note } from '../notes/note.entity';
|
import { Note } from '../notes/note.entity';
|
||||||
|
import { RealtimeNoteModule } from '../realtime/realtime-note/realtime-note.module';
|
||||||
import { UsersModule } from '../users/users.module';
|
import { UsersModule } from '../users/users.module';
|
||||||
import { PermissionsService } from './permissions.service';
|
import { PermissionsService } from './permissions.service';
|
||||||
|
|
||||||
|
@ -18,6 +19,7 @@ import { PermissionsService } from './permissions.service';
|
||||||
UsersModule,
|
UsersModule,
|
||||||
GroupsModule,
|
GroupsModule,
|
||||||
LoggerModule,
|
LoggerModule,
|
||||||
|
forwardRef(() => RealtimeNoteModule),
|
||||||
],
|
],
|
||||||
exports: [PermissionsService],
|
exports: [PermissionsService],
|
||||||
providers: [PermissionsService],
|
providers: [PermissionsService],
|
||||||
|
|
|
@ -34,6 +34,7 @@ import {
|
||||||
import { Note } from '../notes/note.entity';
|
import { Note } from '../notes/note.entity';
|
||||||
import { NotesModule } from '../notes/notes.module';
|
import { NotesModule } from '../notes/notes.module';
|
||||||
import { Tag } from '../notes/tag.entity';
|
import { Tag } from '../notes/tag.entity';
|
||||||
|
import { RealtimeNoteModule } from '../realtime/realtime-note/realtime-note.module';
|
||||||
import { Edit } from '../revisions/edit.entity';
|
import { Edit } from '../revisions/edit.entity';
|
||||||
import { Revision } from '../revisions/revision.entity';
|
import { Revision } from '../revisions/revision.entity';
|
||||||
import { Session } from '../users/session.entity';
|
import { Session } from '../users/session.entity';
|
||||||
|
@ -109,6 +110,7 @@ describe('PermissionsService', () => {
|
||||||
],
|
],
|
||||||
}),
|
}),
|
||||||
GroupsModule,
|
GroupsModule,
|
||||||
|
RealtimeNoteModule,
|
||||||
],
|
],
|
||||||
})
|
})
|
||||||
.overrideProvider(getRepositoryToken(User))
|
.overrideProvider(getRepositoryToken(User))
|
||||||
|
|
|
@ -19,6 +19,8 @@ import { SpecialGroup } from '../groups/groups.special';
|
||||||
import { ConsoleLoggerService } from '../logger/console-logger.service';
|
import { ConsoleLoggerService } from '../logger/console-logger.service';
|
||||||
import { NotePermissionsUpdateDto } from '../notes/note-permissions.dto';
|
import { NotePermissionsUpdateDto } from '../notes/note-permissions.dto';
|
||||||
import { Note } from '../notes/note.entity';
|
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 { User } from '../users/user.entity';
|
||||||
import { UsersService } from '../users/users.service';
|
import { UsersService } from '../users/users.service';
|
||||||
import { checkArrayForDuplicates } from '../utils/arrayDuplicatCheck';
|
import { checkArrayForDuplicates } from '../utils/arrayDuplicatCheck';
|
||||||
|
@ -34,6 +36,8 @@ export class PermissionsService {
|
||||||
private readonly logger: ConsoleLoggerService,
|
private readonly logger: ConsoleLoggerService,
|
||||||
@Inject(noteConfiguration.KEY)
|
@Inject(noteConfiguration.KEY)
|
||||||
private noteConfig: NoteConfig,
|
private noteConfig: NoteConfig,
|
||||||
|
private realtimeNoteService: RealtimeNoteService,
|
||||||
|
private realtimeNoteStore: RealtimeNoteStore,
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -150,6 +154,13 @@ export class PermissionsService {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private notifyOthers(noteId: Note['id']): void {
|
||||||
|
const realtimeNote = this.realtimeNoteStore.find(noteId);
|
||||||
|
if (realtimeNote) {
|
||||||
|
realtimeNote.announcePermissionChange();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @async
|
* @async
|
||||||
* Update a notes permissions.
|
* Update a notes permissions.
|
||||||
|
@ -211,7 +222,7 @@ export class PermissionsService {
|
||||||
createdPermission.note = Promise.resolve(note);
|
createdPermission.note = Promise.resolve(note);
|
||||||
(await note.groupPermissions).push(createdPermission);
|
(await note.groupPermissions).push(createdPermission);
|
||||||
}
|
}
|
||||||
|
this.notifyOthers(note.id);
|
||||||
return await this.noteRepository.save(note);
|
return await this.noteRepository.save(note);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -245,6 +256,7 @@ export class PermissionsService {
|
||||||
);
|
);
|
||||||
(await note.userPermissions).push(noteUserPermission);
|
(await note.userPermissions).push(noteUserPermission);
|
||||||
}
|
}
|
||||||
|
this.notifyOthers(note.id);
|
||||||
return await this.noteRepository.save(note);
|
return await this.noteRepository.save(note);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -264,6 +276,7 @@ export class PermissionsService {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
note.userPermissions = Promise.resolve(newPermissions);
|
note.userPermissions = Promise.resolve(newPermissions);
|
||||||
|
this.notifyOthers(note.id);
|
||||||
return await this.noteRepository.save(note);
|
return await this.noteRepository.save(note);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -305,6 +318,7 @@ export class PermissionsService {
|
||||||
);
|
);
|
||||||
(await note.groupPermissions).push(noteGroupPermission);
|
(await note.groupPermissions).push(noteGroupPermission);
|
||||||
}
|
}
|
||||||
|
this.notifyOthers(note.id);
|
||||||
return await this.noteRepository.save(note);
|
return await this.noteRepository.save(note);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -327,6 +341,7 @@ export class PermissionsService {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
note.groupPermissions = Promise.resolve(newPermissions);
|
note.groupPermissions = Promise.resolve(newPermissions);
|
||||||
|
this.notifyOthers(note.id);
|
||||||
return await this.noteRepository.save(note);
|
return await this.noteRepository.save(note);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -339,6 +354,7 @@ export class PermissionsService {
|
||||||
*/
|
*/
|
||||||
async changeOwner(note: Note, owner: User): Promise<Note> {
|
async changeOwner(note: Note, owner: User): Promise<Note> {
|
||||||
note.owner = Promise.resolve(owner);
|
note.owner = Promise.resolve(owner);
|
||||||
|
this.notifyOthers(note.id);
|
||||||
return await this.noteRepository.save(note);
|
return await this.noteRepository.save(note);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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.
|
* 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.
|
* @throws NotInDBError if note doesn't exist or has no revisions.
|
||||||
* @return A {@link RealtimeNote} that is linked to the given note.
|
* @return A {@link RealtimeNote} that is linked to the given note.
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -3,6 +3,10 @@
|
||||||
*
|
*
|
||||||
* SPDX-License-Identifier: AGPL-3.0-only
|
* SPDX-License-Identifier: AGPL-3.0-only
|
||||||
*/
|
*/
|
||||||
|
import {
|
||||||
|
encodeDocumentDeletedMessage,
|
||||||
|
encodeMetadataUpdatedMessage,
|
||||||
|
} from '@hedgedoc/realtime';
|
||||||
import { Logger } from '@nestjs/common';
|
import { Logger } from '@nestjs/common';
|
||||||
import { EventEmitter } from 'events';
|
import { EventEmitter } from 'events';
|
||||||
import TypedEventEmitter, { EventMap } from 'typed-emitter';
|
import TypedEventEmitter, { EventMap } from 'typed-emitter';
|
||||||
|
@ -130,4 +134,29 @@ export class RealtimeNote extends (EventEmitter as TypedEventEmitterConstructor<
|
||||||
public getNote(): Note {
|
public getNote(): Note {
|
||||||
return this.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);
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue