From d5dc807b0916d26862a2e6c787b67c329a99b5dd Mon Sep 17 00:00:00 2001 From: Gregoire Parant Date: Mon, 21 Dec 2020 16:48:40 +0100 Subject: [PATCH 1/3] Login & Sign in Create html element to log in and sign in --- .../resources/html/gameForgotPassword.html | 105 ++++++ front/dist/resources/html/gameLogin.html | 111 +++++++ front/dist/resources/html/gameMenu.html | 3 + front/dist/resources/html/gameRegister.html | 117 +++++++ front/dist/resources/html/gameShare.html | 106 ++++++ front/src/Connexion/ConnectionManager.ts | 54 ++- front/src/Connexion/LocalUser.ts | 10 + front/src/Exception/ErrorConnectedError.ts | 1 + front/src/Exception/NotConnectedError.ts | 1 + front/src/Phaser/Menu/MenuScene.ts | 311 +++++++++++++++++- 10 files changed, 813 insertions(+), 6 deletions(-) create mode 100644 front/dist/resources/html/gameForgotPassword.html create mode 100644 front/dist/resources/html/gameLogin.html create mode 100644 front/dist/resources/html/gameRegister.html create mode 100644 front/dist/resources/html/gameShare.html create mode 100644 front/src/Exception/ErrorConnectedError.ts create mode 100644 front/src/Exception/NotConnectedError.ts diff --git a/front/dist/resources/html/gameForgotPassword.html b/front/dist/resources/html/gameForgotPassword.html new file mode 100644 index 00000000..691a7d0c --- /dev/null +++ b/front/dist/resources/html/gameForgotPassword.html @@ -0,0 +1,105 @@ + + + + diff --git a/front/dist/resources/html/gameLogin.html b/front/dist/resources/html/gameLogin.html new file mode 100644 index 00000000..98df47ea --- /dev/null +++ b/front/dist/resources/html/gameLogin.html @@ -0,0 +1,111 @@ + + + diff --git a/front/dist/resources/html/gameMenu.html b/front/dist/resources/html/gameMenu.html index 88c76ca2..3a6b833b 100644 --- a/front/dist/resources/html/gameMenu.html +++ b/front/dist/resources/html/gameMenu.html @@ -38,5 +38,8 @@ +
+ +
diff --git a/front/dist/resources/html/gameRegister.html b/front/dist/resources/html/gameRegister.html new file mode 100644 index 00000000..5be17a0c --- /dev/null +++ b/front/dist/resources/html/gameRegister.html @@ -0,0 +1,117 @@ + + + diff --git a/front/dist/resources/html/gameShare.html b/front/dist/resources/html/gameShare.html new file mode 100644 index 00000000..4e487328 --- /dev/null +++ b/front/dist/resources/html/gameShare.html @@ -0,0 +1,106 @@ + + + diff --git a/front/src/Connexion/ConnectionManager.ts b/front/src/Connexion/ConnectionManager.ts index a113a00c..fa09a046 100644 --- a/front/src/Connexion/ConnectionManager.ts +++ b/front/src/Connexion/ConnectionManager.ts @@ -4,15 +4,24 @@ import {RoomConnection} from "./RoomConnection"; import {OnConnectInterface, PositionInterface, ViewportInterface} from "./ConnexionModels"; import {GameConnexionTypes, urlManager} from "../Url/UrlManager"; import {localUserStore} from "./LocalUserStore"; -import {LocalUser} from "./LocalUser"; +import {ConnectedUser, LocalUser} from "./LocalUser"; import {Room} from "./Room"; +import {NotConnectedError} from "../Exception/NotConnectedError"; +import {ErrorConnectedError} from "../Exception/ErrorConnectedError"; const URL_ROOM_STARTED = '/Floor0/floor0.json'; class ConnectionManager { private localUser!:LocalUser; - private connexionType?: GameConnexionTypes + private connexionType?: GameConnexionTypes; + + private connectedUser?: ConnectedUser|null; + + constructor() { + this.verifyLoggedUser(); + } + /** * Tries to login to the node server and return the starting map url to be loaded */ @@ -109,6 +118,47 @@ class ConnectionManager { get getConnexionType(){ return this.connexionType; } + + public verifyLoggedUser(){ + //Verify spark session + Axios.get('http://admin.workadventure.localhost/user/connected').then((res) => { + console.log('res', res); + Axios.get('http://admin.workadventure.localhost/notifications/recent').then((res) => { + this.connectedUser = res.data; + }).catch((err) => { + console.error(err); + this.connectedUser = null; + throw new ErrorConnectedError('Error to get user element'); + }); + }).catch((err) => { + if(err instanceof ErrorConnectedError) { + throw err; + } + console.log('err', err); + this.connectedUser = null; + throw new NotConnectedError('User not connected'); + }); + } + + /** + * RegisterUser + * + * @param name + * @param email + * @param password + * @param confirmPassword + */ + public registerUser(name: string, email: string, password: string, confirmPassword: string){ + Axios.get('http://admin.workadventure.localhost/member/register') + .then((res) => { + this.connectedUser = res.data; + }) + .catch((err) => { + this.connectedUser = null; + console.log(err); + throw err; + }) + } } export const connectionManager = new ConnectionManager(); diff --git a/front/src/Connexion/LocalUser.ts b/front/src/Connexion/LocalUser.ts index 06d98b70..62918814 100644 --- a/front/src/Connexion/LocalUser.ts +++ b/front/src/Connexion/LocalUser.ts @@ -9,3 +9,13 @@ export class LocalUser { constructor(public readonly uuid:string, public readonly jwtToken: string, public readonly textures: CharacterTexture[]) { } } + +export class ConnectedUser { + constructor( + public readonly name:string, + public readonly email: string, + public readonly notification: [], + public readonly announcements: [], + ) { + } +} diff --git a/front/src/Exception/ErrorConnectedError.ts b/front/src/Exception/ErrorConnectedError.ts new file mode 100644 index 00000000..783e93ed --- /dev/null +++ b/front/src/Exception/ErrorConnectedError.ts @@ -0,0 +1 @@ +export class ErrorConnectedError extends Error{} \ No newline at end of file diff --git a/front/src/Exception/NotConnectedError.ts b/front/src/Exception/NotConnectedError.ts new file mode 100644 index 00000000..387a1ce3 --- /dev/null +++ b/front/src/Exception/NotConnectedError.ts @@ -0,0 +1 @@ +export class NotConnectedError extends Error{} \ No newline at end of file diff --git a/front/src/Phaser/Menu/MenuScene.ts b/front/src/Phaser/Menu/MenuScene.ts index e0817d39..559204f4 100644 --- a/front/src/Phaser/Menu/MenuScene.ts +++ b/front/src/Phaser/Menu/MenuScene.ts @@ -9,6 +9,10 @@ export const MenuSceneName = 'MenuScene'; const gameMenuKey = 'gameMenu'; const gameMenuIconKey = 'gameMenuIcon'; const gameSettingsMenuKey = 'gameSettingsMenu'; +const gameLogin = 'gameLogin'; +const gameForgotPassword = 'gameForgotPassword'; +const gameRegister = 'gameRegister'; +const gameShare = 'gameShare'; const closedSideMenuX = -200; const openedSideMenuX = 0; @@ -19,8 +23,14 @@ const openedSideMenuX = 0; export class MenuScene extends Phaser.Scene { private menuElement!: Phaser.GameObjects.DOMElement; private gameQualityMenuElement!: Phaser.GameObjects.DOMElement; + private gameLoginElement!: Phaser.GameObjects.DOMElement; + private gameForgotPasswordElement!: Phaser.GameObjects.DOMElement; + private gameRegisterElement!: Phaser.GameObjects.DOMElement; + private gameShareElement!: Phaser.GameObjects.DOMElement; private sideMenuOpened = false; private settingsMenuOpened = false; + private gameLoginMenuOpened = false; + private gameShareOpened = false; private gameQualityValue: number; private videoQualityValue: number; private menuButton!: Phaser.GameObjects.DOMElement; @@ -36,6 +46,10 @@ export class MenuScene extends Phaser.Scene { this.load.html(gameMenuKey, 'resources/html/gameMenu.html'); this.load.html(gameMenuIconKey, 'resources/html/gameMenuIcon.html'); this.load.html(gameSettingsMenuKey, 'resources/html/gameQualityMenu.html'); + this.load.html(gameLogin, 'resources/html/gameLogin.html'); + this.load.html(gameForgotPassword, 'resources/html/gameForgotPassword.html'); + this.load.html(gameRegister, 'resources/html/gameRegister.html'); + this.load.html(gameShare, 'resources/html/gameShare.html'); } create() { @@ -47,6 +61,62 @@ export class MenuScene extends Phaser.Scene { this.gameQualityMenuElement = this.add.dom(middleX, -400).createFromCache(gameSettingsMenuKey); this.revealMenusAfterInit(this.gameQualityMenuElement, 'gameQuality'); + this.gameLoginElement = this.add.dom(middleX, -400).createFromCache(gameLogin); + this.revealMenusAfterInit(this.gameLoginElement, gameLogin); + this.gameLoginElement.addListener('click'); + this.gameLoginElement.on('click', (event:MouseEvent) => { + event.preventDefault(); + if((event?.target as HTMLInputElement).id === 'gameLoginFormCancel') { + this.closeGameLogin(); + }else if((event?.target as HTMLInputElement).id === 'gameLoginFormSubmit') { + this.login(); + }else if((event?.target as HTMLInputElement).id === 'gameLoginFormForgotPassword') { + this.closeGameLogin(); + this.openGameForgotPassword(); + }else if((event?.target as HTMLInputElement).id === 'gameLoginFormRegister') { + this.closeGameLogin(); + this.openGameRegister(); + } + }); + + this.gameForgotPasswordElement = this.add.dom(middleX, -400).createFromCache(gameForgotPassword); + this.revealMenusAfterInit(this.gameForgotPasswordElement, gameForgotPassword); + this.gameForgotPasswordElement.addListener('click'); + this.gameForgotPasswordElement.on('click', (event:MouseEvent) => { + event.preventDefault(); + if((event?.target as HTMLInputElement).id === 'gameLoginFormForgotPasswordSubmit') { + this.sendEmail(); + }else if((event?.target as HTMLInputElement).id === 'gameLoginFormForgotPasswordCancel') { + this.closeGameForgotPassword(); + this.openGameLogin(); + } + }); + + this.gameRegisterElement = this.add.dom(middleX, -400).createFromCache(gameRegister); + this.revealMenusAfterInit(this.gameRegisterElement, gameRegister); + this.gameRegisterElement.addListener('click'); + this.gameRegisterElement.on('click', (event:MouseEvent) => { + event.preventDefault(); + if((event?.target as HTMLInputElement).id === 'gameRegisterFormSubmit') { + this.register(); + }else if((event?.target as HTMLInputElement).id === 'gameRegisterFormCancel') { + this.closeGameRegister(); + this.openGameLogin(); + } + }); + + this.gameShareElement = this.add.dom(middleX, -400).createFromCache(gameShare); + this.revealMenusAfterInit(this.gameShareElement, gameShare); + this.gameShareElement.addListener('click'); + this.gameShareElement.on('click', (event:MouseEvent) => { + event.preventDefault(); + if((event?.target as HTMLInputElement).id === 'gameShareFormSubmit') { + this.copyLink(); + }else if((event?.target as HTMLInputElement).id === 'gameShareFormCancel') { + this.closeGameShare(); + } + }); + this.input.keyboard.on('keyup-TAB', () => { this.sideMenuOpened ? this.closeSideMenu() : this.openSideMenu(); }); @@ -91,7 +161,11 @@ export class MenuScene extends Phaser.Scene { private closeSideMenu(): void { if (!this.sideMenuOpened) return; this.sideMenuOpened = false; - this.closeGameQualityMenu() + this.closeGameQualityMenu(); + this.closeGameForgotPassword(); + this.closeGameLogin(); + this.closeGameRegister(); + this.closeGameShare(); this.menuButton.getChildByID('openMenuButton').innerHTML = ``; gameManager.getCurrentGameScene(this).ConsoleGlobalMessageManager.disabledMessageConsole(); this.tweens.add({ @@ -158,7 +232,231 @@ export class MenuScene extends Phaser.Scene { }); } + private openGameLogin(): void{ + this.closeGameRegister(); + this.closeGameForgotPassword(); + if (this.gameLoginMenuOpened) { + this.closeGameLogin(); + return; + } + this.gameLoginMenuOpened = true; + let middleY = (window.innerHeight / 3) - (257); + if(middleY < 0){ + middleY = 0; + } + let middleX = (window.innerWidth / 3) - 298; + if(middleX < 0){ + middleX = 0; + } + this.tweens.add({ + targets: this.gameLoginElement, + y: middleY, + x: middleX, + duration: 1000, + ease: 'Power3' + }); + } + + private closeGameLogin(): void{ + this.gameLoginMenuOpened = false; + this.tweens.add({ + targets: this.gameLoginElement, + y: -400, + duration: 1000, + ease: 'Power3' + }); + } + + private openGameShare(): void{ + if (this.gameShareOpened) { + this.closeGameShare(); + return; + } + + const gameShareLink = this.gameShareElement.getChildByID('gameShareLink') as HTMLInputElement; + gameShareLink.value = location.toString(); + + this.gameShareOpened = true; + + let middleY = (window.innerHeight / 3) - (257); + if(middleY < 0){ + middleY = 0; + } + let middleX = (window.innerWidth / 3) - 298; + if(middleX < 0){ + middleX = 0; + } + this.tweens.add({ + targets: this.gameShareElement, + y: middleY, + x: middleX, + duration: 1000, + ease: 'Power3' + }); + } + + private closeGameShare(): void{ + const gameShareInfo = this.gameShareElement.getChildByID('gameShareInfo') as HTMLParagraphElement; + gameShareInfo.innerText = ''; + gameShareInfo.style.display = 'none'; + this.gameShareOpened = false; + this.tweens.add({ + targets: this.gameShareElement, + y: -400, + duration: 1000, + ease: 'Power3' + }); + } + + private login(): void{ + let errorForm = false; + const gameLoginEmail = this.gameLoginElement.getChildByID('gameLoginEmail') as HTMLInputElement; + const gameLoginPassword = this.gameLoginElement.getChildByID('gameLoginPassword') as HTMLInputElement; + const gameLoginError = this.gameLoginElement.getChildByID('gameLoginError') as HTMLInputElement; + + gameLoginError.innerText = ''; + gameLoginError.style.display = 'none'; + + if (!gameLoginEmail.value || gameLoginEmail.value === '') { + gameLoginEmail.style.borderColor = 'red'; + errorForm = true; + }else{ + gameLoginEmail.style.borderColor = 'black'; + } + + if (!gameLoginPassword.value || gameLoginPassword.value === '') { + gameLoginPassword.style.borderColor = 'red'; + errorForm = true; + }else{ + gameLoginPassword.style.borderColor = 'black'; + } + + if(errorForm){return;} + + gameLoginError.innerText = 'Login or password incorrect'; + gameLoginError.style.display = 'block'; + //TODO login user in back + } + + private sendEmail(){ + const gameForgotPasswordInfo = this.gameForgotPasswordElement.getChildByID('gameForgotPasswordInfo') as HTMLParagraphElement; + gameForgotPasswordInfo.style.display = 'none'; + gameForgotPasswordInfo.innerText = ''; + + const gameForgotPasswordError = this.gameForgotPasswordElement.getChildByID('gameForgotPasswordError') as HTMLParagraphElement; + gameForgotPasswordError.style.display = 'none'; + gameForgotPasswordError.innerText = ''; + + const gameLoginForgotPasswordEmail = this.gameForgotPasswordElement.getChildByID('gameLoginForgotPasswordEmail') as HTMLInputElement; + gameLoginForgotPasswordEmail.style.borderColor = 'black'; + if(!gameLoginForgotPasswordEmail.value || gameLoginForgotPasswordEmail.value === ''){ + gameForgotPasswordError.innerText = 'The email field is required.'; + gameForgotPasswordError.style.display = 'block'; + gameLoginForgotPasswordEmail.style.borderColor = 'red'; + return; + } + //TODO send email + gameForgotPasswordInfo.style.display = 'block'; + gameForgotPasswordInfo.innerText = 'We have emailed your password reset link!'; + } + + private register(){ + const gameRegisterName = this.gameRegisterElement.getChildByID('gameRegisterName') as HTMLInputElement; + gameRegisterName.style.borderColor = 'black'; + const gameRegisterEmail = this.gameRegisterElement.getChildByID('gameRegisterEmail') as HTMLInputElement; + gameRegisterEmail.style.borderColor = 'black'; + const gameRegisterPassword = this.gameRegisterElement.getChildByID('gameRegisterPassword') as HTMLInputElement; + gameRegisterPassword.style.borderColor = 'black'; + const gameRegisterConfirmPassword = this.gameRegisterElement.getChildByID('gameRegisterConfirmPassword') as HTMLInputElement; + gameRegisterConfirmPassword.style.borderColor = 'black'; + + let hasError = false; + if(!gameRegisterName.value || gameRegisterName.value === ''){ + gameRegisterName.style.borderColor = 'red'; + hasError = true; + } + if(!gameRegisterEmail.value || gameRegisterEmail.value === ''){ + gameRegisterEmail.style.borderColor = 'red'; + hasError = true; + } + if(!gameRegisterPassword.value || gameRegisterPassword.value === ''){ + gameRegisterPassword.style.borderColor = 'red'; + hasError = true; + } + if( + !gameRegisterConfirmPassword.value + || gameRegisterConfirmPassword.value === '' + || gameRegisterConfirmPassword.value !== gameRegisterPassword.value + ){ + gameRegisterConfirmPassword.style.borderColor = 'red'; + hasError = true; + } + + const gameRegisterInfo = this.gameRegisterElement.getChildByID('gameRegisterInfo') as HTMLParagraphElement; + gameRegisterInfo.style.display = 'none'; + gameRegisterInfo.innerText = ''; + + const gameRegisterError = this.gameRegisterElement.getChildByID('gameRegisterError') as HTMLParagraphElement; + gameRegisterError.style.display = 'none'; + gameRegisterError.innerText = ''; + + if(hasError){return;} + } + + private openGameForgotPassword(): void{ + let middleY = (window.innerHeight / 3) - (257); + if(middleY < 0){ + middleY = 0; + } + let middleX = (window.innerWidth / 3) - 298; + if(middleX < 0){ + middleX = 0; + } + this.tweens.add({ + targets: this.gameForgotPasswordElement, + y: middleY, + x: middleX, + duration: 1000, + ease: 'Power3' + }); + } + + private closeGameForgotPassword(): void{ + this.tweens.add({ + targets: this.gameForgotPasswordElement, + y: -400, + duration: 1000, + ease: 'Power3' + }); + } + + private openGameRegister(): void{ + let middleY = (window.innerHeight / 3) - (257); + if(middleY < 0){ + middleY = 0; + } + let middleX = (window.innerWidth / 3) - 298; + if(middleX < 0){ + middleX = 0; + } + this.tweens.add({ + targets: this.gameRegisterElement, + y: middleY, + x: middleX, + duration: 1000, + ease: 'Power3' + }); + } + + private closeGameRegister(): void{ + this.tweens.add({ + targets: this.gameRegisterElement, + y: -400, + duration: 1000, + ease: 'Power3' + }); + } private onMenuClick(event:MouseEvent) { event.preventDefault(); @@ -179,7 +477,7 @@ export class MenuScene extends Phaser.Scene { this.closeSideMenu(); break; case 'shareButton': - this.shareUrl(); + this.openGameShare(); break; case 'editGameSettingsButton': this.openGameSettingsMenu(); @@ -187,12 +485,17 @@ export class MenuScene extends Phaser.Scene { case 'adminConsoleButton': gameManager.getCurrentGameScene(this).ConsoleGlobalMessageManager.activeMessageConsole(); break; + case 'gameLoginMenu': + this.openGameLogin(); + break; } } - private async shareUrl() { + private async copyLink() { await navigator.clipboard.writeText(location.toString()); - alert('URL is copied to your clipboard!'); + const gameShareInfo = this.gameShareElement.getChildByID('gameShareInfo') as HTMLParagraphElement; + gameShareInfo.innerText = 'Link copied, you can share it now!'; + gameShareInfo.style.display = 'block'; } private saveSetting(valueGame: number, valueVideo: number){ From 53b96d61fe8acc1deca39375ecee71fb9f2d1043 Mon Sep 17 00:00:00 2001 From: Gregoire Parant Date: Mon, 21 Dec 2020 19:43:57 +0100 Subject: [PATCH 2/3] Finish login and reset password --- front/dist/resources/html/gameLogin.html | 2 +- front/dist/resources/html/gameMenuIcon.html | 1 + front/dist/resources/html/gameRegister.html | 2 +- front/src/Connexion/ConnectionManager.ts | 50 +++++--- front/src/Phaser/Menu/MenuScene.ts | 57 ++++++--- .../src/Controller/AuthenticateController.ts | 118 ++++++++++++++++++ pusher/src/Enum/EnvironmentVariable.ts | 2 +- pusher/src/Services/AdminApi.ts | 55 ++++++++ 8 files changed, 256 insertions(+), 31 deletions(-) diff --git a/front/dist/resources/html/gameLogin.html b/front/dist/resources/html/gameLogin.html index 98df47ea..5f751492 100644 --- a/front/dist/resources/html/gameLogin.html +++ b/front/dist/resources/html/gameLogin.html @@ -10,7 +10,7 @@ background: #eceeee; border: 1px solid #42464b; border-radius: 6px; - height: 257px; + height: 270px; margin: 20px auto 0; width: 298px; } diff --git a/front/dist/resources/html/gameMenuIcon.html b/front/dist/resources/html/gameMenuIcon.html index 75a52ac7..83cf225a 100644 --- a/front/dist/resources/html/gameMenuIcon.html +++ b/front/dist/resources/html/gameMenuIcon.html @@ -16,6 +16,7 @@ #menuIcon button img{ width: 14px; padding-top: 4px; + cursor: url('/resources/logos/cursor_pointer.png'), pointer; } #menuIcon section { margin: 10px; diff --git a/front/dist/resources/html/gameRegister.html b/front/dist/resources/html/gameRegister.html index 5be17a0c..d33fd7e1 100644 --- a/front/dist/resources/html/gameRegister.html +++ b/front/dist/resources/html/gameRegister.html @@ -112,6 +112,6 @@
- + Login
diff --git a/front/src/Connexion/ConnectionManager.ts b/front/src/Connexion/ConnectionManager.ts index fa09a046..446b1176 100644 --- a/front/src/Connexion/ConnectionManager.ts +++ b/front/src/Connexion/ConnectionManager.ts @@ -19,7 +19,7 @@ class ConnectionManager { private connectedUser?: ConnectedUser|null; constructor() { - this.verifyLoggedUser(); + //this.userLogin(); } /** @@ -119,17 +119,16 @@ class ConnectionManager { return this.connexionType; } - public verifyLoggedUser(){ + /** + * + * @param email + * @param password + */ + public userLogin(email: string, password: string){ //Verify spark session - Axios.get('http://admin.workadventure.localhost/user/connected').then((res) => { - console.log('res', res); - Axios.get('http://admin.workadventure.localhost/notifications/recent').then((res) => { - this.connectedUser = res.data; - }).catch((err) => { - console.error(err); - this.connectedUser = null; - throw new ErrorConnectedError('Error to get user element'); - }); + //TODO change url addresse + return Axios.post('http://pusher.workadventure.localhost/user/login',{email, password}).then((res) => { + return res.data; }).catch((err) => { if(err instanceof ErrorConnectedError) { throw err; @@ -146,10 +145,33 @@ class ConnectionManager { * @param name * @param email * @param password - * @param confirmPassword */ - public registerUser(name: string, email: string, password: string, confirmPassword: string){ - Axios.get('http://admin.workadventure.localhost/member/register') + public registerUser(name: string, email: string, password: string){ + //TODO change url addresse + Axios.post('http://pusher.workadventure.localhost/member/register', { + name, + email, + password + }) + .then((res) => { + this.connectedUser = res.data; + }) + .catch((err) => { + this.connectedUser = null; + console.log(err); + throw err; + }) + } + + /** + * + * @param email + */ + public passwordReset(email: string) { + //TODO change url addresse + return Axios.post('http://pusher.workadventure.localhost/user/password/reset', { + email, + }) .then((res) => { this.connectedUser = res.data; }) diff --git a/front/src/Phaser/Menu/MenuScene.ts b/front/src/Phaser/Menu/MenuScene.ts index 559204f4..4ce72f42 100644 --- a/front/src/Phaser/Menu/MenuScene.ts +++ b/front/src/Phaser/Menu/MenuScene.ts @@ -4,6 +4,7 @@ import {gameManager} from "../Game/GameManager"; import {localUserStore} from "../../Connexion/LocalUserStore"; import {mediaManager} from "../../WebRtc/MediaManager"; import {coWebsiteManager} from "../../WebRtc/CoWebsiteManager"; +import {connectionManager} from "../../Connexion/ConnectionManager"; export const MenuSceneName = 'MenuScene'; const gameMenuKey = 'gameMenu'; @@ -144,6 +145,7 @@ export class MenuScene extends Phaser.Scene { openSideMenu() { if (this.sideMenuOpened) return; + this.closeAll(); this.sideMenuOpened = true; this.menuButton.getChildByID('openMenuButton').innerHTML = 'X'; if (gameManager.getCurrentGameScene(this).connection && gameManager.getCurrentGameScene(this).connection.isAdmin()) { @@ -161,12 +163,7 @@ export class MenuScene extends Phaser.Scene { private closeSideMenu(): void { if (!this.sideMenuOpened) return; this.sideMenuOpened = false; - this.closeGameQualityMenu(); - this.closeGameForgotPassword(); - this.closeGameLogin(); - this.closeGameRegister(); - this.closeGameShare(); - this.menuButton.getChildByID('openMenuButton').innerHTML = ``; + this.closeAll(); gameManager.getCurrentGameScene(this).ConsoleGlobalMessageManager.disabledMessageConsole(); this.tweens.add({ targets: this.menuElement, @@ -176,13 +173,13 @@ export class MenuScene extends Phaser.Scene { }); } - - private openGameSettingsMenu(): void { if (this.settingsMenuOpened) { - this.closeGameQualityMenu(); return; } + //close all + this.closeAll(); + this.settingsMenuOpened = true; const gameQualitySelect = this.gameQualityMenuElement.getChildByID('select-game-quality') as HTMLInputElement; @@ -233,12 +230,11 @@ export class MenuScene extends Phaser.Scene { } private openGameLogin(): void{ - this.closeGameRegister(); - this.closeGameForgotPassword(); if (this.gameLoginMenuOpened) { this.closeGameLogin(); return; } + this.closeAll(); this.gameLoginMenuOpened = true; let middleY = (window.innerHeight / 3) - (257); @@ -273,6 +269,8 @@ export class MenuScene extends Phaser.Scene { this.closeGameShare(); return; } + //close all + this.closeAll(); const gameShareLink = this.gameShareElement.getChildByID('gameShareLink') as HTMLInputElement; gameShareLink.value = location.toString(); @@ -313,7 +311,7 @@ export class MenuScene extends Phaser.Scene { let errorForm = false; const gameLoginEmail = this.gameLoginElement.getChildByID('gameLoginEmail') as HTMLInputElement; const gameLoginPassword = this.gameLoginElement.getChildByID('gameLoginPassword') as HTMLInputElement; - const gameLoginError = this.gameLoginElement.getChildByID('gameLoginError') as HTMLInputElement; + const gameLoginError = this.gameLoginElement.getChildByID('gameLoginError') as HTMLParagraphElement; gameLoginError.innerText = ''; gameLoginError.style.display = 'none'; @@ -337,8 +335,16 @@ export class MenuScene extends Phaser.Scene { gameLoginError.innerText = 'Login or password incorrect'; gameLoginError.style.display = 'block'; //TODO login user in back + + connectionManager.userLogin(gameLoginEmail.value, gameLoginPassword.value).then(() => { + this.closeGameLogin(); + }).catch((err) => { + gameLoginError.innerText = err.message; + gameLoginError.style.display = 'block'; + }); } + private sendEmail(){ const gameForgotPasswordInfo = this.gameForgotPasswordElement.getChildByID('gameForgotPasswordInfo') as HTMLParagraphElement; gameForgotPasswordInfo.style.display = 'none'; @@ -357,8 +363,16 @@ export class MenuScene extends Phaser.Scene { return; } //TODO send email - gameForgotPasswordInfo.style.display = 'block'; - gameForgotPasswordInfo.innerText = 'We have emailed your password reset link!'; + connectionManager.passwordReset(gameLoginForgotPasswordEmail.value).then(() => { + gameForgotPasswordInfo.style.display = 'block'; + gameForgotPasswordInfo.innerText = 'We have emailed your password reset link!'; + gameLoginForgotPasswordEmail.value = ''; + this.closeGameLogin(); + }).catch((err) => { + gameForgotPasswordError.innerText = err.message; + gameForgotPasswordError.style.display = 'block'; + gameLoginForgotPasswordEmail.value = ''; + }); } private register(){ @@ -405,6 +419,9 @@ export class MenuScene extends Phaser.Scene { } private openGameForgotPassword(): void{ + //close all + this.closeAll(); + let middleY = (window.innerHeight / 3) - (257); if(middleY < 0){ middleY = 0; @@ -432,6 +449,9 @@ export class MenuScene extends Phaser.Scene { } private openGameRegister(): void{ + //close all + this.closeAll(); + let middleY = (window.innerHeight / 3) - (257); if(middleY < 0){ middleY = 0; @@ -516,4 +536,13 @@ export class MenuScene extends Phaser.Scene { const sparkHost = 'https://'+window.location.host.replace('play.', '')+'/choose-map.html'; window.open(sparkHost, '_blank'); } + + private closeAll(){ + this.closeGameQualityMenu(); + this.closeGameForgotPassword(); + this.closeGameLogin(); + this.closeGameRegister(); + this.closeGameShare(); + this.menuButton.getChildByID('openMenuButton').innerHTML = ``; + } } diff --git a/pusher/src/Controller/AuthenticateController.ts b/pusher/src/Controller/AuthenticateController.ts index bf68768d..ccf09bb1 100644 --- a/pusher/src/Controller/AuthenticateController.ts +++ b/pusher/src/Controller/AuthenticateController.ts @@ -16,6 +16,9 @@ export class AuthenticateController extends BaseController { this.register(); this.verify(); this.anonymLogin(); + this.userRegister(); + this.userLogin(); + this.forgotPassword(); } //Try to login with an admin token @@ -132,4 +135,119 @@ export class AuthenticateController extends BaseController { })); }); } + + private userLogin(){ + this.App.options("/user/login", (res: HttpResponse, req: HttpRequest) => { + this.addCorsHeaders(res); + res.end(); + }); + + this.App.post("/user/login", (res: HttpResponse, req: HttpRequest) => { + + (async () => { + res.onAborted(() => { + console.warn('Login request was aborted'); + }) + + let userUuid = ''; + try { + const params = await res.json(); + + const response = await adminApi.loginUser(params.email as string, params.password as string); + userUuid = response.data.uuid as string; + const authToken = jwtTokenManager.createJWTToken(userUuid); + res.writeStatus("200 OK"); + this.addCorsHeaders(res); + res.end(JSON.stringify({ + authToken, + userUuid, + user: response.data + })); + }catch (err){ + res.writeStatus("400 KO"); + this.addCorsHeaders(res); + res.end(JSON.stringify({ + message: 'Email or password incorrect' + })); + } + })(); + }); + } + + private userRegister(){ + this.App.options("/user/register", (res: HttpResponse, req: HttpRequest) => { + this.addCorsHeaders(res); + res.end(); + }); + + this.App.post("/user/register", (res: HttpResponse, req: HttpRequest) => { + + (async () => { + + res.onAborted(() => { + console.warn('Register request was aborted'); + }) + + let userUuid = ''; + try { + const params = await res.json(); + + const response = await adminApi.register( + params.name as string, + params.email as string, + params.password as string + ); + userUuid = response.data.uuid as string; + const authToken = jwtTokenManager.createJWTToken(userUuid); + res.writeStatus("200 OK"); + this.addCorsHeaders(res); + res.end(JSON.stringify({ + authToken, + userUuid, + user: response.data + })); + }catch (err){ + res.writeStatus("400 KO"); + this.addCorsHeaders(res); + res.end(JSON.stringify({ + message: err.message + })); + } + })(); + }); + } + + private forgotPassword() { + this.App.options("/user/password/reset", (res: HttpResponse, req: HttpRequest) => { + this.addCorsHeaders(res); + res.end(); + }); + + this.App.post("/user/password/reset", (res: HttpResponse, req: HttpRequest) => { + + (async () => { + + res.onAborted(() => { + console.warn('Forgot password request was aborted'); + }); + + try { + const params = await res.json(); + await adminApi.forgotPassword(params.email as string); + res.writeStatus("200 OK"); + this.addCorsHeaders(res); + res.end(JSON.stringify({ + message: 'Email sent!' + })); + } catch (err) { + res.writeStatus("400 KO"); + this.addCorsHeaders(res); + res.end(JSON.stringify({ + message: err.message + })); + } + + })(); + }); + } } diff --git a/pusher/src/Enum/EnvironmentVariable.ts b/pusher/src/Enum/EnvironmentVariable.ts index 62ebccc1..fb8b9e2a 100644 --- a/pusher/src/Enum/EnvironmentVariable.ts +++ b/pusher/src/Enum/EnvironmentVariable.ts @@ -4,7 +4,7 @@ const MINIMUM_DISTANCE = process.env.MINIMUM_DISTANCE ? Number(process.env.MINIM const GROUP_RADIUS = process.env.GROUP_RADIUS ? Number(process.env.GROUP_RADIUS) : 48; const ALLOW_ARTILLERY = process.env.ALLOW_ARTILLERY ? process.env.ALLOW_ARTILLERY == 'true' : false; const API_URL = process.env.API_URL || ''; -const ADMIN_API_URL = process.env.ADMIN_API_URL || ''; +const ADMIN_API_URL = process.env.ADMIN_API_URL || 'http://admin'; const ADMIN_API_TOKEN = process.env.ADMIN_API_TOKEN || 'myapitoken'; const MAX_USERS_PER_ROOM = parseInt(process.env.MAX_USERS_PER_ROOM || '') || 600; const CPU_OVERHEAT_THRESHOLD = Number(process.env.CPU_OVERHEAT_THRESHOLD) || 80; diff --git a/pusher/src/Services/AdminApi.ts b/pusher/src/Services/AdminApi.ts index 9c46a41b..27dbab59 100644 --- a/pusher/src/Services/AdminApi.ts +++ b/pusher/src/Services/AdminApi.ts @@ -110,6 +110,61 @@ class AdminApi { headers: {"Authorization": `${ADMIN_API_TOKEN}`} }); } + + /** + * + * @param email + * @param password + */ + loginUser(email: string, password: string) { + console.log('email', email); + console.log('password', password); + return Axios.post(`${ADMIN_API_URL}/api/user/login`, { + email, + password, + }, + { + headers: {"Authorization": `${ADMIN_API_TOKEN}`} + }); + } + + /** + * + * @param name + * @param email + * @param password + */ + register( + name: string, + email: string, + password: string, + ) { + return Axios.post(`${ADMIN_API_URL}/api/user/register`, { + name, + email, + password, + }, + { + headers: {"Authorization": `${ADMIN_API_TOKEN}`} + }); + } + + /** + * + * @param name + * @param email + * @param password + */ + forgotPassword( + email: string + ) { + return Axios.post(`${ADMIN_API_URL}/api/user/password/reset`, { + email + }, + { + headers: {"Authorization": `${ADMIN_API_TOKEN}`} + }); + } } export const adminApi = new AdminApi(); From 69bf416dd0269c2c8b5ea24a84e9f0dd4be252ea Mon Sep 17 00:00:00 2001 From: Gregoire Parant Date: Tue, 22 Dec 2020 11:15:18 +0100 Subject: [PATCH 3/3] Create URL to get user notification --- front/src/Connexion/ConnectionManager.ts | 42 +++++++++++++++---- front/src/Connexion/LocalUser.ts | 14 ++++++- front/src/Connexion/LocalUserStore.ts | 15 ++++++- front/src/Exception/ErrorConnectedError.ts | 1 - front/src/Phaser/Menu/MenuScene.ts | 5 --- .../src/Controller/AuthenticateController.ts | 32 ++++++++++++++ pusher/src/Services/AdminApi.ts | 7 ++++ pusher/src/Services/JWTTokenManager.ts | 8 +++- 8 files changed, 104 insertions(+), 20 deletions(-) delete mode 100644 front/src/Exception/ErrorConnectedError.ts diff --git a/front/src/Connexion/ConnectionManager.ts b/front/src/Connexion/ConnectionManager.ts index 446b1176..bb0729b8 100644 --- a/front/src/Connexion/ConnectionManager.ts +++ b/front/src/Connexion/ConnectionManager.ts @@ -7,7 +7,6 @@ import {localUserStore} from "./LocalUserStore"; import {ConnectedUser, LocalUser} from "./LocalUser"; import {Room} from "./Room"; import {NotConnectedError} from "../Exception/NotConnectedError"; -import {ErrorConnectedError} from "../Exception/ErrorConnectedError"; const URL_ROOM_STARTED = '/Floor0/floor0.json'; @@ -19,7 +18,8 @@ class ConnectionManager { private connectedUser?: ConnectedUser|null; constructor() { - //this.userLogin(); + this.connectedUser = localUserStore.getUserConnected(); + this.getNotification(); } /** @@ -124,17 +124,39 @@ class ConnectionManager { * @param email * @param password */ - public userLogin(email: string, password: string){ + public userLogin(email: string, password: string) { //Verify spark session //TODO change url addresse - return Axios.post('http://pusher.workadventure.localhost/user/login',{email, password}).then((res) => { - return res.data; + + return Axios.post('http://pusher.workadventure.localhost/user/login', {email, password}).then((res) => { + const user = res.data; + this.localUser = new LocalUser(res.data.userUuid, res.data.authToken, res.data.textures || []); + localUserStore.saveUser(this.localUser); + this.connectedUser = new ConnectedUser( + user.name, + user.email, + user.uuid, + user.jwtToken, + [], + [] + ); + localUserStore.saveUserConnected(this.connectedUser); + + return this.getNotification().then((response) => { + if (!this.connectedUser) { + return; + } + this.connectedUser.setNotification(response.data.notification || []); + this.connectedUser.setAnnouncements(response.data.announcements || []); + localUserStore.saveUserConnected(this.connectedUser); + }).catch((err) => { + console.info(err); + }); + }).catch((err) => { - if(err instanceof ErrorConnectedError) { - throw err; - } console.log('err', err); this.connectedUser = null; + localUserStore.clearUserConnected(); throw new NotConnectedError('User not connected'); }); } @@ -181,6 +203,10 @@ class ConnectionManager { throw err; }) } + + private getNotification(){ + return Axios.get('http://pusher.workadventure.localhost/user/notifications/recent'); + } } export const connectionManager = new ConnectionManager(); diff --git a/front/src/Connexion/LocalUser.ts b/front/src/Connexion/LocalUser.ts index 62918814..287ccdfd 100644 --- a/front/src/Connexion/LocalUser.ts +++ b/front/src/Connexion/LocalUser.ts @@ -14,8 +14,18 @@ export class ConnectedUser { constructor( public readonly name:string, public readonly email: string, - public readonly notification: [], - public readonly announcements: [], + public readonly uuid:string, + public readonly jwtToken: string, + public notification?: [], + public announcements?: [], ) { } + + setNotification(notification: []){ + this.notification = notification; + } + + setAnnouncements(announcements: []){ + this.announcements = announcements; + } } diff --git a/front/src/Connexion/LocalUserStore.ts b/front/src/Connexion/LocalUserStore.ts index 8ac8c7b2..dd5b1191 100644 --- a/front/src/Connexion/LocalUserStore.ts +++ b/front/src/Connexion/LocalUserStore.ts @@ -1,4 +1,4 @@ -import {LocalUser} from "./LocalUser"; +import {ConnectedUser, LocalUser} from "./LocalUser"; const characterLayersKey = 'characterLayers'; const gameQualityKey = 'gameQuality'; @@ -6,7 +6,7 @@ const videoQualityKey = 'videoQuality'; //todo: add localstorage fallback class LocalUserStore { - + saveUser(localUser: LocalUser) { localStorage.setItem('localUser', JSON.stringify(localUser)); } @@ -56,6 +56,17 @@ class LocalUserStore { setVideoQualityValue(value: number): void { localStorage.setItem(videoQualityKey, '' + value); } + + clearUserConnected(){ + localStorage.removeItem('connectedUser'); + } + saveUserConnected(connectedUser: ConnectedUser) { + localStorage.setItem('connectedUser', JSON.stringify(connectedUser)); + } + getUserConnected(): ConnectedUser|null { + const data = localStorage.getItem('connectedUser'); + return data ? JSON.parse(data) : null; + } } export const localUserStore = new LocalUserStore(); \ No newline at end of file diff --git a/front/src/Exception/ErrorConnectedError.ts b/front/src/Exception/ErrorConnectedError.ts deleted file mode 100644 index 783e93ed..00000000 --- a/front/src/Exception/ErrorConnectedError.ts +++ /dev/null @@ -1 +0,0 @@ -export class ErrorConnectedError extends Error{} \ No newline at end of file diff --git a/front/src/Phaser/Menu/MenuScene.ts b/front/src/Phaser/Menu/MenuScene.ts index 4ce72f42..7ecd53b2 100644 --- a/front/src/Phaser/Menu/MenuScene.ts +++ b/front/src/Phaser/Menu/MenuScene.ts @@ -331,11 +331,6 @@ export class MenuScene extends Phaser.Scene { } if(errorForm){return;} - - gameLoginError.innerText = 'Login or password incorrect'; - gameLoginError.style.display = 'block'; - //TODO login user in back - connectionManager.userLogin(gameLoginEmail.value, gameLoginPassword.value).then(() => { this.closeGameLogin(); }).catch((err) => { diff --git a/pusher/src/Controller/AuthenticateController.ts b/pusher/src/Controller/AuthenticateController.ts index ccf09bb1..b714a5f4 100644 --- a/pusher/src/Controller/AuthenticateController.ts +++ b/pusher/src/Controller/AuthenticateController.ts @@ -19,6 +19,7 @@ export class AuthenticateController extends BaseController { this.userRegister(); this.userLogin(); this.forgotPassword(); + this.notificationRecent(); } //Try to login with an admin token @@ -250,4 +251,35 @@ export class AuthenticateController extends BaseController { })(); }); } + + private notificationRecent() { + this.App.options("/user/notifications/recent", (res: HttpResponse, req: HttpRequest) => { + this.addCorsHeaders(res); + res.end(); + }); + + this.App.get("/user/notifications/recent", (res: HttpResponse, req: HttpRequest) => { + + (async () => { + + res.onAborted(() => { + console.warn('Forgot password request was aborted'); + }); + + try { + const response = await adminApi.getNotification(); + res.writeStatus("200 OK"); + this.addCorsHeaders(res); + res.end(JSON.stringify(response.data)); + } catch (err) { + res.writeStatus("400 KO"); + this.addCorsHeaders(res); + res.end(JSON.stringify({ + message: err.message + })); + } + + })(); + }); + } } diff --git a/pusher/src/Services/AdminApi.ts b/pusher/src/Services/AdminApi.ts index 27dbab59..18b3e081 100644 --- a/pusher/src/Services/AdminApi.ts +++ b/pusher/src/Services/AdminApi.ts @@ -165,6 +165,13 @@ class AdminApi { headers: {"Authorization": `${ADMIN_API_TOKEN}`} }); } + + getNotification() { + return Axios.get(`${ADMIN_API_URL}/notifications/recent`, + { + headers: {"Authorization": `${ADMIN_API_TOKEN}`} + }); + } } export const adminApi = new AdminApi(); diff --git a/pusher/src/Services/JWTTokenManager.ts b/pusher/src/Services/JWTTokenManager.ts index 8abb0e45..015a69f2 100644 --- a/pusher/src/Services/JWTTokenManager.ts +++ b/pusher/src/Services/JWTTokenManager.ts @@ -6,8 +6,12 @@ import {adminApi, AdminApiData} from "../Services/AdminApi"; class JWTTokenManager { - public createJWTToken(userUuid: string) { - return Jwt.sign({userUuid: userUuid}, SECRET_KEY, {expiresIn: '200d'}); //todo: add a mechanic to refresh or recreate token + public createJWTToken(userUuid: string, email?: string, name?: string) { + return Jwt.sign({ + userUuid: userUuid, + email: email || null, + name: name || null, + }, SECRET_KEY, {expiresIn: '200d'}); //todo: add a mechanic to refresh or recreate token } public async getUserUuidFromToken(token: unknown): Promise {