From bf9dfcc835f3a311a92272ffc33d43860065d3bf Mon Sep 17 00:00:00 2001 From: Gregoire Parant Date: Mon, 19 Oct 2020 19:32:47 +0200 Subject: [PATCH 1/7] Ban mesage - Create type message could be use to send private message at any user - Create SendMessageUser message - Add sound when user receive ban message --- back/src/Controller/IoSocketController.ts | 18 ++++- back/src/Services/SocketManager.ts | 24 ++++++- front/dist/index.html | 3 + .../dist/resources/objects/report-message.mp3 | Bin 0 -> 63528 bytes front/dist/resources/style/style.css | 2 + front/src/Administration/TypeMessage.ts | 62 ++++++++++++++++++ .../src/Administration/UserMessageManager.ts | 36 ++++++++++ front/src/Connexion/ConnexionModels.ts | 1 + front/src/Connexion/RoomConnection.ts | 13 ++-- front/src/Phaser/Game/GameScene.ts | 3 + messages/messages.proto | 6 ++ 11 files changed, 160 insertions(+), 8 deletions(-) create mode 100644 front/dist/resources/objects/report-message.mp3 create mode 100644 front/src/Administration/TypeMessage.ts create mode 100644 front/src/Administration/UserMessageManager.ts diff --git a/back/src/Controller/IoSocketController.ts b/back/src/Controller/IoSocketController.ts index 707df4a6..75ee3064 100644 --- a/back/src/Controller/IoSocketController.ts +++ b/back/src/Controller/IoSocketController.ts @@ -12,8 +12,7 @@ import { WebRtcSignalToServerMessage, PlayGlobalMessage, ReportPlayerMessage, - QueryJitsiJwtMessage, - SendJitsiJwtMessage, + QueryJitsiJwtMessage } from "../Messages/generated/messages_pb"; import {UserMovesMessage} from "../Messages/generated/messages_pb"; import {TemplatedApp} from "uWebSockets.js" @@ -72,7 +71,20 @@ export class IoSocketController { clientEventsEmitter.registerToClientLeave(ws.clientLeaveCallback); }, message: (ws, arrayBuffer, isBinary): void => { - console.log('m', ws); //todo: add admin actions such as ban here + try { + //TODO refactor message type and data + let message: {event: string, message: {type: string, message: unknown, userUuid: string}} = + JSON.parse(new TextDecoder("utf-8").decode(new Uint8Array(arrayBuffer))); + + if(message.event === 'user-message') { + if (message.message.type === 'ban') { + let messageToEmit = (message.message as {message: string, type: string, userUuid: string}); + socketManager.emitSendUserMessage(messageToEmit); + } + } + }catch (err) { + console.error(err); + } }, close: (ws, code, message) => { //todo make sure this code unregister the right listeners diff --git a/back/src/Services/SocketManager.ts b/back/src/Services/SocketManager.ts index 44579123..a6204941 100644 --- a/back/src/Services/SocketManager.ts +++ b/back/src/Services/SocketManager.ts @@ -19,7 +19,11 @@ import { UserMovesMessage, ViewportMessage, WebRtcDisconnectMessage, WebRtcSignalToClientMessage, - WebRtcSignalToServerMessage, WebRtcStartMessage, QueryJitsiJwtMessage, SendJitsiJwtMessage + WebRtcSignalToServerMessage, + WebRtcStartMessage, + QueryJitsiJwtMessage, + SendJitsiJwtMessage, + SendUserMessage } from "../Messages/generated/messages_pb"; import {PointInterface} from "../Model/Websocket/PointInterface"; import {User} from "../Model/User"; @@ -668,6 +672,24 @@ class SocketManager { client.send(serverToClientMessage.serializeBinary().buffer, true); } + + public emitSendUserMessage(messageToSend: {userUuid: string, message: string, type: string}): void { + let socket = this.searchClientByUuid(messageToSend.userUuid); + if(!socket){ + throw 'socket was not found'; + } + + const sendUserMessage = new SendUserMessage(); + sendUserMessage.setMessage(messageToSend.message); + sendUserMessage.setType(messageToSend.type); + + const serverToClientMessage = new ServerToClientMessage(); + serverToClientMessage.setSendusermessage(sendUserMessage); + + if (!socket.disconnecting) { + socket.send(serverToClientMessage.serializeBinary().buffer, true); + } + } } export const socketManager = new SocketManager(); diff --git a/front/dist/index.html b/front/dist/index.html index 5984af7b..8e957965 100644 --- a/front/dist/index.html +++ b/front/dist/index.html @@ -125,6 +125,9 @@ + diff --git a/front/dist/resources/objects/report-message.mp3 b/front/dist/resources/objects/report-message.mp3 new file mode 100644 index 0000000000000000000000000000000000000000..0135bfaf712d73678f58bf01959d9f6fb7f2238b GIT binary patch literal 63528 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

PyT-Za(_f;2&bHqv-E65Oq^1Pzu1*T!9gG~N)L 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'main-container'); + mainSectionDiv.appendChild(div); + + const reportMessageAudio = HtmlUtils.getElementByIdOrFail('audio-webrtc-in'); + reportMessageAudio.play(); + + this.nbSecond = this.maxNbSecond; + setTimeout((c) => { + this.forMessage(title); + }, 1000); + } + + forMessage(title: HTMLParagraphElement){ + this.nbSecond -= 1; + title.innerText = `${this.titleMessage} (${this.nbSecond})`; + if(this.nbSecond > 0){ + setTimeout(() => { + this.forMessage(title); + }, 1000); + }else{ + title.innerText = this.titleMessage; + + let imgCancel : HTMLImageElement = document.createElement('img'); + imgCancel.id = 'cancel-report-user'; + imgCancel.src = 'resources/logos/close.svg'; + + const div = HtmlUtils.getElementByIdOrFail('report-message-user'); + div.appendChild(imgCancel); + imgCancel.addEventListener('click', () => { + div.remove(); + }); + } + } +} \ No newline at end of file diff --git a/front/src/Administration/UserMessageManager.ts b/front/src/Administration/UserMessageManager.ts new file mode 100644 index 00000000..87395c14 --- /dev/null +++ b/front/src/Administration/UserMessageManager.ts @@ -0,0 +1,36 @@ +import {RoomConnection} from "../Connexion/RoomConnection"; +import * as TypeMessages from "./TypeMessage"; + +export interface TypeMessageInterface{ + showMessage(message: string) : void; +}; + +export class UserMessageManager { + + typeMessages : Map = new Map(); + + constructor(private Connection: RoomConnection) { + let valueTypeMessageTab = Object.values(TypeMessages); + Object.keys(TypeMessages).forEach((value: string, index: number) => { + let typeMessageInstance : TypeMessageInterface = (new valueTypeMessageTab[index]() as TypeMessageInterface); + this.typeMessages.set(value.toLowerCase(), typeMessageInstance); + }); + this.initialise(); + } + + initialise(){ + //receive signal to show message + this.Connection.receiveUserMessage((type: string, message: string) => { + this.showMessage(type, message); + }); + } + + showMessage(type: string, message: string){ + let classTypeMessage = this.typeMessages.get(type.toLowerCase()); + if(!classTypeMessage){ + console.error('Message unknown'); + return; + } + classTypeMessage.showMessage(message); + } +}; \ No newline at end of file diff --git a/front/src/Connexion/ConnexionModels.ts b/front/src/Connexion/ConnexionModels.ts index 375e1ded..fd2149c5 100644 --- a/front/src/Connexion/ConnexionModels.ts +++ b/front/src/Connexion/ConnexionModels.ts @@ -27,6 +27,7 @@ export enum EventMessage{ STOP_GLOBAL_MESSAGE = "stop-global-message", TELEPORT = "teleport", + USER_MESSAGE = "user-message", START_JITSI_ROOM = "start-jitsi-room", } diff --git a/front/src/Connexion/RoomConnection.ts b/front/src/Connexion/RoomConnection.ts index 2d2d2cf8..9d04cedd 100644 --- a/front/src/Connexion/RoomConnection.ts +++ b/front/src/Connexion/RoomConnection.ts @@ -22,7 +22,7 @@ import { WebRtcSignalToServerMessage, WebRtcStartMessage, ReportPlayerMessage, - TeleportMessageMessage, QueryJitsiJwtMessage, SendJitsiJwtMessage + TeleportMessageMessage, QueryJitsiJwtMessage, SendJitsiJwtMessage, SendUserMessage } from "../Messages/generated/messages_pb" import {UserSimplePeerInterface} from "../WebRtc/SimplePeer"; @@ -35,8 +35,6 @@ import { RoomJoinedMessageInterface, ViewportInterface, WebRtcDisconnectMessageInterface, WebRtcSignalReceivedMessageInterface, - WebRtcSignalSentMessageInterface, - WebRtcStartMessageInterface } from "./ConnexionModels"; export class RoomConnection implements RoomConnection { @@ -152,6 +150,8 @@ export class RoomConnection implements RoomConnection { this.dispatch(EventMessage.TELEPORT, message.getTeleportmessagemessage()); } else if (message.hasSendjitsijwtmessage()) { this.dispatch(EventMessage.START_JITSI_ROOM, message.getSendjitsijwtmessage()); + } else if (message.hasSendusermessage()) { + this.dispatch(EventMessage.USER_MESSAGE, message.getSendusermessage()); } else { throw new Error('Unknown message received'); } @@ -479,8 +479,13 @@ export class RoomConnection implements RoomConnection { }); } + public receiveUserMessage(callback: (type: string, message: string) => void) { + return this.onMessage(EventMessage.USER_MESSAGE, (message: SendUserMessage) => { + callback(message.getType(), message.getMessage()); + }); + } + public emitGlobalMessage(message: PlayGlobalMessageInterface){ - console.log('emitGlobalMessage', message); const playGlobalMessage = new PlayGlobalMessage(); playGlobalMessage.setId(message.id); playGlobalMessage.setType(message.type); diff --git a/front/src/Phaser/Game/GameScene.ts b/front/src/Phaser/Game/GameScene.ts index 2eb34ca0..f82a6ce2 100644 --- a/front/src/Phaser/Game/GameScene.ts +++ b/front/src/Phaser/Game/GameScene.ts @@ -51,6 +51,7 @@ import {ProtobufClientUtils} from "../../Network/ProtobufClientUtils"; import {connectionManager} from "../../Connexion/ConnectionManager"; import {RoomConnection} from "../../Connexion/RoomConnection"; import {GlobalMessageManager} from "../../Administration/GlobalMessageManager"; +import {UserMessageManager} from "../../Administration/UserMessageManager"; import {ConsoleGlobalMessageManager} from "../../Administration/ConsoleGlobalMessageManager"; import {ResizableScene} from "../Login/ResizableScene"; import {Room} from "../../Connexion/Room"; @@ -114,6 +115,7 @@ export class GameScene extends ResizableScene implements CenterListener { private connection!: RoomConnection; private simplePeer!: SimplePeer; private GlobalMessageManager!: GlobalMessageManager; + private UserMessageManager!: UserMessageManager; private ConsoleGlobalMessageManager!: ConsoleGlobalMessageManager; private connectionAnswerPromise: Promise; private connectionAnswerPromiseResolve!: (value?: RoomJoinedMessageInterface | PromiseLike) => void; @@ -600,6 +602,7 @@ export class GameScene extends ResizableScene implements CenterListener { // When connection is performed, let's connect SimplePeer this.simplePeer = new SimplePeer(this.connection, !this.room.isPublic); this.GlobalMessageManager = new GlobalMessageManager(this.connection); + this.UserMessageManager = new UserMessageManager(this.connection); const self = this; this.simplePeer.registerPeerConnectionListener({ diff --git a/messages/messages.proto b/messages/messages.proto index 450def24..45872f22 100644 --- a/messages/messages.proto +++ b/messages/messages.proto @@ -178,6 +178,11 @@ message SendJitsiJwtMessage { string jwt = 2; } +message SendUserMessage{ + string type = 1; + string message = 2; +} + message ServerToClientMessage { oneof message { BatchMessage batchMessage = 1; @@ -191,5 +196,6 @@ message ServerToClientMessage { StopGlobalMessage stopGlobalMessage = 9; TeleportMessageMessage teleportMessageMessage = 10; SendJitsiJwtMessage sendJitsiJwtMessage = 11; + SendUserMessage sendUserMessage = 12; } } From dfa6d2cc6679887dfbca6b1eb081ce682de05fa7 Mon Sep 17 00:00:00 2001 From: Gregoire Parant Date: Mon, 19 Oct 2020 20:49:30 +0200 Subject: [PATCH 2/7] Verify user in admin - If 404, user don't exist in admin, it will be anonym user - if 403, user is ban or not associate in the world --- back/src/Services/AdminApi.ts | 11 +++++++++++ back/src/Services/JWTTokenManager.ts | 15 +++++++++++++-- 2 files changed, 24 insertions(+), 2 deletions(-) diff --git a/back/src/Services/AdminApi.ts b/back/src/Services/AdminApi.ts index c9b40f03..5f6647bf 100644 --- a/back/src/Services/AdminApi.ts +++ b/back/src/Services/AdminApi.ts @@ -61,6 +61,17 @@ class AdminApi { ) return res.data; } + + async fetchCheckUserByToken(organizationMemberToken: string): Promise { + if (!ADMIN_API_URL) { + return Promise.reject('No admin backoffice set!'); + } + //todo: this call can fail if the corresponding world is not activated or if the token is invalid. Handle that case. + const res = await Axios.get(ADMIN_API_URL+'/api/check-user/'+organizationMemberToken, + { headers: {"Authorization" : `${ADMIN_API_TOKEN}`} } + ) + return res.data; + } reportPlayer(reportedUserUuid: string, reportedUserComment: string, reporterUserUuid: string) { return Axios.post(`${ADMIN_API_URL}/api/report`, { diff --git a/back/src/Services/JWTTokenManager.ts b/back/src/Services/JWTTokenManager.ts index 580140b0..94016b21 100644 --- a/back/src/Services/JWTTokenManager.ts +++ b/back/src/Services/JWTTokenManager.ts @@ -2,6 +2,7 @@ import {ALLOW_ARTILLERY, SECRET_KEY} from "../Enum/EnvironmentVariable"; import {uuid} from "uuidv4"; import Jwt from "jsonwebtoken"; import {TokenInterface} from "../Controller/AuthenticateController"; +import {adminApi, AdminApiData} from "../Services/AdminApi"; class JWTTokenManager { @@ -32,7 +33,7 @@ class JWTTokenManager { const tokenInterface = tokenDecoded as TokenInterface; if (err) { console.error('An authentication error happened, invalid JsonWebToken.', err); - reject(new Error('An authentication error happened, invalid JsonWebToken. '+err.message)); + reject(new Error('An authentication error happened, invalid JsonWebToken. ' + err.message)); return; } if (tokenDecoded === undefined) { @@ -41,12 +42,22 @@ class JWTTokenManager { return; } + //verify token if (!this.isValidToken(tokenInterface)) { reject(new Error('Authentication error, invalid token structure.')); return; } - resolve(tokenInterface.userUuid); + //verify user in admin + return adminApi.fetchCheckUserByToken(tokenInterface.userUuid).then(() => { + resolve(tokenInterface.userUuid); + }).catch((err) => { + //anonymous user + if(err.response && err.response.status && err.response.status === 404){ + return resolve(tokenInterface.userUuid); + } + reject(new Error('Authentication error, invalid token structure. ' + err)); + }); }); }); } From ba9f9dcbe14d3eb5fa267047dcf54fe643289611 Mon Sep 17 00:00:00 2001 From: Gregoire Parant Date: Mon, 19 Oct 2020 21:04:16 +0200 Subject: [PATCH 3/7] Fix CD --- back/src/Controller/IoSocketController.ts | 4 ++-- back/src/Services/JWTTokenManager.ts | 5 +++-- back/src/Services/SocketManager.ts | 2 +- front/src/Administration/TypeMessage.ts | 10 ++++----- .../src/Administration/UserMessageManager.ts | 22 +++++++++---------- 5 files changed, 22 insertions(+), 21 deletions(-) diff --git a/back/src/Controller/IoSocketController.ts b/back/src/Controller/IoSocketController.ts index 75ee3064..cc02d201 100644 --- a/back/src/Controller/IoSocketController.ts +++ b/back/src/Controller/IoSocketController.ts @@ -73,12 +73,12 @@ export class IoSocketController { message: (ws, arrayBuffer, isBinary): void => { try { //TODO refactor message type and data - let message: {event: string, message: {type: string, message: unknown, userUuid: string}} = + const message: {event: string, message: {type: string, message: unknown, userUuid: string}} = JSON.parse(new TextDecoder("utf-8").decode(new Uint8Array(arrayBuffer))); if(message.event === 'user-message') { if (message.message.type === 'ban') { - let messageToEmit = (message.message as {message: string, type: string, userUuid: string}); + const messageToEmit = (message.message as {message: string, type: string, userUuid: string}); socketManager.emitSendUserMessage(messageToEmit); } } diff --git a/back/src/Services/JWTTokenManager.ts b/back/src/Services/JWTTokenManager.ts index 94016b21..f82fa001 100644 --- a/back/src/Services/JWTTokenManager.ts +++ b/back/src/Services/JWTTokenManager.ts @@ -49,12 +49,13 @@ class JWTTokenManager { } //verify user in admin - return adminApi.fetchCheckUserByToken(tokenInterface.userUuid).then(() => { + adminApi.fetchCheckUserByToken(tokenInterface.userUuid).then(() => { resolve(tokenInterface.userUuid); }).catch((err) => { //anonymous user if(err.response && err.response.status && err.response.status === 404){ - return resolve(tokenInterface.userUuid); + resolve(tokenInterface.userUuid); + return; } reject(new Error('Authentication error, invalid token structure. ' + err)); }); diff --git a/back/src/Services/SocketManager.ts b/back/src/Services/SocketManager.ts index a6204941..f32557b0 100644 --- a/back/src/Services/SocketManager.ts +++ b/back/src/Services/SocketManager.ts @@ -674,7 +674,7 @@ class SocketManager { } public emitSendUserMessage(messageToSend: {userUuid: string, message: string, type: string}): void { - let socket = this.searchClientByUuid(messageToSend.userUuid); + const socket = this.searchClientByUuid(messageToSend.userUuid); if(!socket){ throw 'socket was not found'; } diff --git a/front/src/Administration/TypeMessage.ts b/front/src/Administration/TypeMessage.ts index 41b30133..20360fea 100644 --- a/front/src/Administration/TypeMessage.ts +++ b/front/src/Administration/TypeMessage.ts @@ -7,21 +7,21 @@ export class Ban implements TypeMessageInterface { private titleMessage = 'IMPORTANT !'; showMessage(message: string): void { - let div : HTMLDivElement = document.createElement('div'); + const div : HTMLDivElement = document.createElement('div'); div.classList.add('modal-report-user'); div.id = 'report-message-user'; div.style.backgroundColor = '#000000e0'; - let img : HTMLImageElement = document.createElement('img'); + const img : HTMLImageElement = document.createElement('img'); img.src = 'resources/logos/report.svg'; div.appendChild(img); - let title : HTMLParagraphElement = document.createElement('p'); + const title : HTMLParagraphElement = document.createElement('p'); title.id = 'title-report-user'; title.innerText = `${this.titleMessage} (${this.maxNbSecond})`; div.appendChild(title); - let p : HTMLParagraphElement = document.createElement('p'); + const p : HTMLParagraphElement = document.createElement('p'); p.id = 'body-report-user' p.innerText = message; div.appendChild(p); @@ -48,7 +48,7 @@ export class Ban implements TypeMessageInterface { }else{ title.innerText = this.titleMessage; - let imgCancel : HTMLImageElement = document.createElement('img'); + const imgCancel : HTMLImageElement = document.createElement('img'); imgCancel.id = 'cancel-report-user'; imgCancel.src = 'resources/logos/close.svg'; diff --git a/front/src/Administration/UserMessageManager.ts b/front/src/Administration/UserMessageManager.ts index 87395c14..12022b03 100644 --- a/front/src/Administration/UserMessageManager.ts +++ b/front/src/Administration/UserMessageManager.ts @@ -1,36 +1,36 @@ import {RoomConnection} from "../Connexion/RoomConnection"; import * as TypeMessages from "./TypeMessage"; -export interface TypeMessageInterface{ - showMessage(message: string) : void; -}; +export interface TypeMessageInterface { + showMessage(message: string): void; +} export class UserMessageManager { - typeMessages : Map = new Map(); + typeMessages: Map = new Map(); constructor(private Connection: RoomConnection) { - let valueTypeMessageTab = Object.values(TypeMessages); + const valueTypeMessageTab = Object.values(TypeMessages); Object.keys(TypeMessages).forEach((value: string, index: number) => { - let typeMessageInstance : TypeMessageInterface = (new valueTypeMessageTab[index]() as TypeMessageInterface); + const typeMessageInstance: TypeMessageInterface = (new valueTypeMessageTab[index]() as TypeMessageInterface); this.typeMessages.set(value.toLowerCase(), typeMessageInstance); }); this.initialise(); } - initialise(){ + initialise() { //receive signal to show message this.Connection.receiveUserMessage((type: string, message: string) => { this.showMessage(type, message); }); } - showMessage(type: string, message: string){ - let classTypeMessage = this.typeMessages.get(type.toLowerCase()); - if(!classTypeMessage){ + showMessage(type: string, message: string) { + const classTypeMessage = this.typeMessages.get(type.toLowerCase()); + if (!classTypeMessage) { console.error('Message unknown'); return; } classTypeMessage.showMessage(message); } -}; \ No newline at end of file +} \ No newline at end of file From 7059c6e6e34b2c2ff69e27253e8913f9d752ee20 Mon Sep 17 00:00:00 2001 From: Gregoire Parant Date: Tue, 20 Oct 2020 08:20:21 +0200 Subject: [PATCH 4/7] Refactor message --- back/src/Controller/IoSocketController.ts | 7 +++++++ back/src/Services/SocketManager.ts | 3 ++- front/src/Administration/TypeMessage.ts | 7 ++++++- 3 files changed, 15 insertions(+), 2 deletions(-) diff --git a/back/src/Controller/IoSocketController.ts b/back/src/Controller/IoSocketController.ts index cc02d201..c4d882a2 100644 --- a/back/src/Controller/IoSocketController.ts +++ b/back/src/Controller/IoSocketController.ts @@ -81,6 +81,13 @@ export class IoSocketController { const messageToEmit = (message.message as {message: string, type: string, userUuid: string}); socketManager.emitSendUserMessage(messageToEmit); } + if (message.message.type === 'banned') { + const messageToEmit = (message.message as {message: string, type: string, userUuid: string}); + const socketUser = socketManager.emitSendUserMessage(messageToEmit); + setTimeout(() => { + socketUser.close(); + }, 10000); + } } }catch (err) { console.error(err); diff --git a/back/src/Services/SocketManager.ts b/back/src/Services/SocketManager.ts index f32557b0..9fd343be 100644 --- a/back/src/Services/SocketManager.ts +++ b/back/src/Services/SocketManager.ts @@ -673,7 +673,7 @@ class SocketManager { client.send(serverToClientMessage.serializeBinary().buffer, true); } - public emitSendUserMessage(messageToSend: {userUuid: string, message: string, type: string}): void { + public emitSendUserMessage(messageToSend: {userUuid: string, message: string, type: string}): ExSocketInterface { const socket = this.searchClientByUuid(messageToSend.userUuid); if(!socket){ throw 'socket was not found'; @@ -689,6 +689,7 @@ class SocketManager { if (!socket.disconnecting) { socket.send(serverToClientMessage.serializeBinary().buffer, true); } + return socket; } } diff --git a/front/src/Administration/TypeMessage.ts b/front/src/Administration/TypeMessage.ts index 20360fea..09b87eec 100644 --- a/front/src/Administration/TypeMessage.ts +++ b/front/src/Administration/TypeMessage.ts @@ -1,7 +1,7 @@ import {TypeMessageInterface} from "./UserMessageManager"; import {HtmlUtils} from "../WebRtc/HtmlUtils"; -export class Ban implements TypeMessageInterface { +export class TypeMessageExt implements TypeMessageInterface{ private nbSecond = 0; private maxNbSecond = 10; private titleMessage = 'IMPORTANT !'; @@ -59,4 +59,9 @@ export class Ban implements TypeMessageInterface { }); } } +} +export class Ban extends TypeMessageExt { +} + +export class Banned extends TypeMessageExt { } \ No newline at end of file From eb1f62bb1cc2120762ae7c4254e30b8a926b1595 Mon Sep 17 00:00:00 2001 From: Gregoire Parant Date: Tue, 20 Oct 2020 08:30:11 +0200 Subject: [PATCH 5/7] Add banned message and close ws --- back/src/Controller/IoSocketController.ts | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/back/src/Controller/IoSocketController.ts b/back/src/Controller/IoSocketController.ts index c4d882a2..0425aa40 100644 --- a/back/src/Controller/IoSocketController.ts +++ b/back/src/Controller/IoSocketController.ts @@ -77,16 +77,17 @@ export class IoSocketController { JSON.parse(new TextDecoder("utf-8").decode(new Uint8Array(arrayBuffer))); if(message.event === 'user-message') { - if (message.message.type === 'ban') { - const messageToEmit = (message.message as {message: string, type: string, userUuid: string}); - socketManager.emitSendUserMessage(messageToEmit); - } - if (message.message.type === 'banned') { - const messageToEmit = (message.message as {message: string, type: string, userUuid: string}); - const socketUser = socketManager.emitSendUserMessage(messageToEmit); - setTimeout(() => { - socketUser.close(); - }, 10000); + const messageToEmit = (message.message as { message: string, type: string, userUuid: string }); + switch (message.message.type) { + case 'ban': + socketManager.emitSendUserMessage(messageToEmit); + break; + case 'banned': + const socketUser = socketManager.emitSendUserMessage(messageToEmit); + setTimeout(() => { + socketUser.close(); + }, 10000); + break; } } }catch (err) { From 8d1d6fc8dc3f1efef1ec3baf19f181160edadcd7 Mon Sep 17 00:00:00 2001 From: Gregoire Parant Date: Tue, 20 Oct 2020 09:20:00 +0200 Subject: [PATCH 6/7] Push message when user is connected on new room --- back/src/Controller/IoSocketController.ts | 19 ++++++++++++++++++- back/src/Services/AdminApi.ts | 8 +++++--- front/src/Administration/TypeMessage.ts | 2 +- 3 files changed, 24 insertions(+), 5 deletions(-) diff --git a/back/src/Controller/IoSocketController.ts b/back/src/Controller/IoSocketController.ts index 0425aa40..22d11f03 100644 --- a/back/src/Controller/IoSocketController.ts +++ b/back/src/Controller/IoSocketController.ts @@ -18,7 +18,7 @@ import {UserMovesMessage} from "../Messages/generated/messages_pb"; import {TemplatedApp} from "uWebSockets.js" import {parse} from "query-string"; import {jwtTokenManager} from "../Services/JWTTokenManager"; -import {adminApi} from "../Services/AdminApi"; +import {adminApi, fetchMemberDataByUuidResponse} from "../Services/AdminApi"; import {socketManager} from "../Services/SocketManager"; import {emitInBatch, resetPing} from "../Services/IoSocketHelpers"; import Jwt from "jsonwebtoken"; @@ -226,6 +226,23 @@ export class IoSocketController { const client = this.initClient(ws); //todo: into the upgrade instead? socketManager.handleJoinRoom(client); resetPing(client); + + //get data information and shwo messages + adminApi.fetchMemberDataByUuid(client.userUuid).then((res: fetchMemberDataByUuidResponse) => { + if (!res.messages) { + return; + } + res.messages.forEach((c: unknown) => { + let messageToSend = c as { type: string, message: string }; + socketManager.emitSendUserMessage({ + userUuid: client.userUuid, + type: messageToSend.type, + message: messageToSend.message + }) + }); + }).catch((err) => { + console.error('fetchMemberDataByUuid => err', err); + }); }, message: (ws, arrayBuffer, isBinary): void => { const client = ws as ExSocketInterface; diff --git a/back/src/Services/AdminApi.ts b/back/src/Services/AdminApi.ts index 5f6647bf..9f51fb2e 100644 --- a/back/src/Services/AdminApi.ts +++ b/back/src/Services/AdminApi.ts @@ -9,11 +9,13 @@ export interface AdminApiData { tags: string[] policy_type: number userUuid: string + messages?: unknown[] } export interface fetchMemberDataByUuidResponse { uuid: string; tags: string[]; + messages: unknown[]; } class AdminApi { @@ -32,9 +34,9 @@ class AdminApi { params.roomSlug = roomSlug; } - const res = await Axios.get(ADMIN_API_URL+'/api/map', + const res = await Axios.get(ADMIN_API_URL + '/api/map', { - headers: {"Authorization" : `${ADMIN_API_TOKEN}`}, + headers: {"Authorization": `${ADMIN_API_TOKEN}`}, params } ) @@ -45,7 +47,7 @@ class AdminApi { if (!ADMIN_API_URL) { return Promise.reject('No admin backoffice set!'); } - const res = await Axios.get(ADMIN_API_URL+'/membership/'+uuid, + const res = await Axios.get(ADMIN_API_URL+'/api/membership/'+uuid, { headers: {"Authorization" : `${ADMIN_API_TOKEN}`} } ) return res.data; diff --git a/front/src/Administration/TypeMessage.ts b/front/src/Administration/TypeMessage.ts index 09b87eec..7bd9b484 100644 --- a/front/src/Administration/TypeMessage.ts +++ b/front/src/Administration/TypeMessage.ts @@ -29,7 +29,7 @@ export class TypeMessageExt implements TypeMessageInterface{ const mainSectionDiv = HtmlUtils.getElementByIdOrFail('main-container'); mainSectionDiv.appendChild(div); - const reportMessageAudio = HtmlUtils.getElementByIdOrFail('audio-webrtc-in'); + const reportMessageAudio = HtmlUtils.getElementByIdOrFail('report-message'); reportMessageAudio.play(); this.nbSecond = this.maxNbSecond; From 565ce6b70b87bd69feb354b7f79ddc23091303a5 Mon Sep 17 00:00:00 2001 From: Gregoire Parant Date: Tue, 20 Oct 2020 09:24:06 +0200 Subject: [PATCH 7/7] eslint fix --- back/src/Controller/IoSocketController.ts | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/back/src/Controller/IoSocketController.ts b/back/src/Controller/IoSocketController.ts index 22d11f03..49b08bb7 100644 --- a/back/src/Controller/IoSocketController.ts +++ b/back/src/Controller/IoSocketController.ts @@ -79,15 +79,20 @@ export class IoSocketController { if(message.event === 'user-message') { const messageToEmit = (message.message as { message: string, type: string, userUuid: string }); switch (message.message.type) { - case 'ban': + case 'ban': { socketManager.emitSendUserMessage(messageToEmit); break; - case 'banned': + } + case 'banned': { const socketUser = socketManager.emitSendUserMessage(messageToEmit); setTimeout(() => { socketUser.close(); }, 10000); break; + } + default: { + break; + } } } }catch (err) { @@ -233,7 +238,7 @@ export class IoSocketController { return; } res.messages.forEach((c: unknown) => { - let messageToSend = c as { type: string, message: string }; + const messageToSend = c as { type: string, message: string }; socketManager.emitSendUserMessage({ userUuid: client.userUuid, type: messageToSend.type,