Compare commits

...

3 Commits

Author SHA1 Message Date
Gregoire Parant 69bf416dd0 Create URL to get user notification 2020-12-22 11:15:18 +01:00
Gregoire Parant 53b96d61fe Finish login and reset password 2020-12-21 19:43:57 +01:00
Gregoire Parant d5dc807b09 Login & Sign in
Create html element to log in and sign in
2020-12-21 16:48:40 +01:00
15 changed files with 1131 additions and 15 deletions

View File

@ -0,0 +1,105 @@
<style>
*{
font-family: 'Open Sans', sans-serif;
cursor: url('/resources/logos/cursor_normal.png'), auto;
}
* a, button, input{
cursor: url('/resources/logos/cursor_pointer.png'), pointer;
}
#gameForgotPassword {
background: #eceeee;
border: 1px solid #42464b;
border-radius: 6px;
margin: 20px auto 0;
width: 298px;
height: 160px;
}
#gameForgotPassword h1 {
background-image: linear-gradient(top, #f1f3f3, #d4dae0);
border-bottom: 1px solid #a6abaf;
border-radius: 6px 6px 0 0;
box-sizing: border-box;
color: #727678;
display: block;
height: 43px;
padding-top: 10px;
margin: 0;
text-align: center;
text-shadow: 0 -1px 0 rgba(0,0,0,0.2), 0 1px 0 #fff;
}
#gameForgotPassword input {
font-size: 70%;
background: linear-gradient(top, #d6d7d7, #dee0e0);
border: 1px solid #a1a3a3;
border-radius: 4px;
box-shadow: 0 1px #fff;
box-sizing: border-box;
color: #696969;
height: 30px;
transition: box-shadow 0.3s;
width: 100%;
}
#gameForgotPassword section {
margin: 10px;
}
#gameForgotPassword section.action{
text-align: center;
margin: 0;
}
#gameForgotPassword button {
margin-top: 10px;
background-color: black;
color: white;
border-radius: 7px;
padding-bottom: 4px;
width: 60px;
}
#gameForgotPassword button#gameLoginFormForgotPasswordCancel {
background-color: #c7c7c700;
color: #292929;
}
#gameForgotPassword section a{
text-align: center;
font-size: 12px;
margin: 0 6px;
color: black;
}
#gameForgotPassword section h6,
#gameForgotPassword section h5{
margin: 1px;
}
#gameForgotPassword section.text-center{
text-align: center;
}
#gameForgotPassword section p{
font-size: 8px;
margin: 0px 70px;
}
#gameForgotPassword section p.err{
color: red;
display: none;
}
#gameForgotPassword section p.info{
display: none;
}
#gameForgotPassword button#gameLoginFormForgotPasswordSubmit{
width: 90px;
}
</style>
<form id="gameForgotPassword" hidden>
<section class="text-center">
<h5>Reset Password</h5>
<p class="info" id="gameForgotPasswordInfo"></p>
<p class="err" id="gameForgotPasswordError"></p>
</section>
<section>
<h6>E-Mail Address*</h6>
<input type="email" name="email" id="gameLoginForgotPasswordEmail">
</section>
<section class="action">
<button type="submit" id="gameLoginFormForgotPasswordSubmit">Send email</button>
<button type="submit" id="gameLoginFormForgotPasswordCancel">Login</button>
</section>
</form>

111
front/dist/resources/html/gameLogin.html vendored Normal file
View File

@ -0,0 +1,111 @@
<style>
*{
font-family: 'Open Sans', sans-serif;
cursor: url('/resources/logos/cursor_normal.png'), auto;
}
* a, button, input{
cursor: url('/resources/logos/cursor_pointer.png'), pointer;
}
#gameLogin {
background: #eceeee;
border: 1px solid #42464b;
border-radius: 6px;
height: 270px;
margin: 20px auto 0;
width: 298px;
}
#gameLogin .cautiousText {
font-size: 50%;
}
#gameLogin h1 {
background-image: linear-gradient(top, #f1f3f3, #d4dae0);
border-bottom: 1px solid #a6abaf;
border-radius: 6px 6px 0 0;
box-sizing: border-box;
color: #727678;
display: block;
height: 43px;
padding-top: 10px;
margin: 0;
text-align: center;
text-shadow: 0 -1px 0 rgba(0,0,0,0.2), 0 1px 0 #fff;
}
#gameLogin input {
font-size: 70%;
background: linear-gradient(top, #d6d7d7, #dee0e0);
border: 1px solid #a1a3a3;
border-radius: 4px;
box-shadow: 0 1px #fff;
box-sizing: border-box;
color: #696969;
height: 30px;
transition: box-shadow 0.3s;
width: 100%;
}
#gameLogin section {
margin: 10px;
}
#gameLogin section.action{
text-align: center;
margin: 0;
}
#gameLogin button {
margin-top: 10px;
background-color: black;
color: white;
border-radius: 7px;
padding-bottom: 4px;
width: 60px;
}
#gameLogin button#gameLoginFormCancel {
background-color: #c7c7c700;
color: #292929;
}
#gameLogin section a{
text-align: center;
font-size: 12px;
margin: 0 6px;
color: black;
}
#gameLogin section h6,
#gameLogin section h5{
margin: 1px;
}
#gameLogin section.text-center{
text-align: center;
}
#gameLogin section p{
font-size: 8px;
margin: 0px 70px;
}
#gameLogin section p.err{
color: red;
display: none;
}
</style>
<form id="gameLogin" hidden>
<section class="text-center">
<h5>Login WorkAdventure</h5>
<p>Log in WorkAdventure and find your friends / colleagues</p>
<p class="err" id="gameLoginError"></p>
</section>
<section>
<h6>Email*</h6>
<input type="email" name="email" id="gameLoginEmail">
</section>
<section>
<h6>Password*</h6>
<input type="password" name="password" id="gameLoginPassword">
</section>
<section class="text-center" style="margin: 0;">
<a href="#" target="register" id="gameLoginFormForgotPassword">Forgot Password</a>
</section>
<section class="action">
<button type="submit" id="gameLoginFormSubmit">Login</button>
</section>
<section class="text-center" style="margin: 0;">
<a href="#" target="register" id="gameLoginFormRegister">Sign in</a>
<a href="#" target="anonymous" id="gameLoginFormCancel">Anonymous</a>
</section>
</form>

View File

@ -38,5 +38,8 @@
<section id="adminConsoleSection" hidden>
<button id="adminConsoleButton">Admin console</button>
</section>
<section>
<button id="gameLoginMenu">Login</button>
</section>
</main>
</div>

View File

@ -16,6 +16,7 @@
#menuIcon button img{
width: 14px;
padding-top: 4px;
cursor: url('/resources/logos/cursor_pointer.png'), pointer;
}
#menuIcon section {
margin: 10px;

View File

@ -0,0 +1,117 @@
<style>
*{
font-family: 'Open Sans', sans-serif;
cursor: url('/resources/logos/cursor_normal.png'), auto;
}
* a, button, input{
cursor: url('/resources/logos/cursor_pointer.png'), pointer;
}
#gameRegister {
background: #eceeee;
border: 1px solid #42464b;
border-radius: 6px;
margin: 20px auto 0;
width: 298px;
height: 340px;
}
#gameRegister .cautiousText {
font-size: 50%;
}
#gameRegister h1 {
background-image: linear-gradient(top, #f1f3f3, #d4dae0);
border-bottom: 1px solid #a6abaf;
border-radius: 6px 6px 0 0;
box-sizing: border-box;
color: #727678;
display: block;
height: 43px;
padding-top: 10px;
margin: 0;
text-align: center;
text-shadow: 0 -1px 0 rgba(0,0,0,0.2), 0 1px 0 #fff;
}
#gameRegister input {
font-size: 70%;
background: linear-gradient(top, #d6d7d7, #dee0e0);
border: 1px solid #a1a3a3;
border-radius: 4px;
box-shadow: 0 1px #fff;
box-sizing: border-box;
color: #696969;
height: 30px;
transition: box-shadow 0.3s;
width: 100%;
}
#gameRegister section {
margin: 10px;
}
#gameRegister section.action{
text-align: center;
margin: 0;
}
#gameRegister button {
margin-top: 10px;
background-color: black;
color: white;
border-radius: 7px;
padding-bottom: 4px;
width: 60px;
}
#gameRegister button#gameRegisterFormCancel {
background-color: #c7c7c700;
color: #292929;
}
#gameRegister section a{
text-align: center;
font-size: 12px;
margin: 0 6px;
color: black;
}
#gameRegister section h6,
#gameRegister section h5{
margin: 1px;
}
#gameRegister section.text-center{
text-align: center;
}
#gameRegister section p{
font-size: 8px;
margin: 0px 70px;
}
#gameRegister section p.err{
color: red;
display: none;
}
#gameRegister section p.info{
display: none;
}
</style>
<form id="gameRegister" hidden>
<section class="text-center">
<h5>Sign in WorkAdventure</h5>
<p>Sign in WorkAdventure and share your friends / colleagues</p>
<p class="err" id="gameRegisterError"></p>
<p class="info" id="gameRegisterInfo"></p>
</section>
<section>
<h6>Name*</h6>
<input type="text" name="email" id="gameRegisterName">
</section>
<section>
<h6>Email*</h6>
<input type="email" name="email" id="gameRegisterEmail">
</section>
<section>
<h6>Password*</h6>
<input type="password" name="password" id="gameRegisterPassword">
</section>
<section>
<h6>Confirm password*</h6>
<input type="password" name="password" id="gameRegisterConfirmPassword">
</section>
<section class="action">
<button type="submit" id="gameRegisterFormSubmit">Sign In</button>
<a href="#" id="gameRegisterFormCancel">Login</a>
</section>
</form>

106
front/dist/resources/html/gameShare.html vendored Normal file
View File

@ -0,0 +1,106 @@
<style>
*{
font-family: 'Open Sans', sans-serif;
cursor: url('/resources/logos/cursor_normal.png'), auto;
}
* a, button, input{
cursor: url('/resources/logos/cursor_pointer.png'), pointer;
}
#gameShare {
background: #eceeee;
border: 1px solid #42464b;
border-radius: 6px;
margin: 20px auto 0;
width: 298px;
height: 150px;
}
#gameShare .cautiousText {
font-size: 50%;
}
#gameShare h1 {
background-image: linear-gradient(top, #f1f3f3, #d4dae0);
border-bottom: 1px solid #a6abaf;
border-radius: 6px 6px 0 0;
box-sizing: border-box;
color: #727678;
display: block;
height: 43px;
padding-top: 10px;
margin: 0;
text-align: center;
text-shadow: 0 -1px 0 rgba(0,0,0,0.2), 0 1px 0 #fff;
}
#gameShare input {
font-size: 70%;
background: linear-gradient(top, #d6d7d7, #dee0e0);
border: 1px solid #a1a3a3;
border-radius: 4px;
box-shadow: 0 1px #fff;
box-sizing: border-box;
color: #696969;
height: 30px;
transition: box-shadow 0.3s;
width: 100%;
}
#gameShare section {
margin: 10px;
}
#gameShare section.action{
text-align: center;
margin: 0;
}
#gameShare button {
margin-top: 10px;
background-color: black;
color: white;
border-radius: 7px;
padding-bottom: 4px;
width: 60px;
}
#gameShare button#gameShareFormCancel {
background-color: #c7c7c700;
color: #292929;
}
#gameShare section a{
text-align: center;
font-size: 12px;
margin: 0 6px;
color: black;
}
#gameShare section h6,
#gameShare section h5{
margin: 1px;
}
#gameShare section.text-center{
text-align: center;
}
#gameShare section p{
font-size: 8px;
margin: 0px 70px;
}
#gameShare section p.err{
color: red;
display: none;
}
#gameShare section p.info{
display: none;
}
#gameShare section input#gameShareLink{
background-color: #a1a3a3;
}
</style>
<form id="gameShare" hidden>
<section class="text-center">
<h5>Share this link !</h5>
<p class="info" id="gameShareInfo"></p>
</section>
<section>
<h6>Link</h6>
<input type="text" name="email" id="gameShareLink" readonly>
</section>
<section class="action">
<button type="submit" id="gameShareFormSubmit">Copy</button>
<button type="submit" id="gameShareFormCancel">Close</button>
</section>
</form>

View File

@ -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";
const URL_ROOM_STARTED = '/Floor0/floor0.json';
class ConnectionManager {
private localUser!:LocalUser;
private connexionType?: GameConnexionTypes
private connexionType?: GameConnexionTypes;
private connectedUser?: ConnectedUser|null;
constructor() {
this.connectedUser = localUserStore.getUserConnected();
this.getNotification();
}
/**
* Tries to login to the node server and return the starting map url to be loaded
*/
@ -109,6 +118,95 @@ class ConnectionManager {
get getConnexionType(){
return this.connexionType;
}
/**
*
* @param email
* @param password
*/
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) => {
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) => {
console.log('err', err);
this.connectedUser = null;
localUserStore.clearUserConnected();
throw new NotConnectedError('User not connected');
});
}
/**
* RegisterUser
*
* @param name
* @param email
* @param password
*/
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;
})
.catch((err) => {
this.connectedUser = null;
console.log(err);
throw err;
})
}
private getNotification(){
return Axios.get('http://pusher.workadventure.localhost/user/notifications/recent');
}
}
export const connectionManager = new ConnectionManager();

View File

@ -9,3 +9,23 @@ 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 uuid:string,
public readonly jwtToken: string,
public notification?: [],
public announcements?: [],
) {
}
setNotification(notification: []){
this.notification = notification;
}
setAnnouncements(announcements: []){
this.announcements = announcements;
}
}

View File

@ -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();

View File

@ -0,0 +1 @@
export class NotConnectedError extends Error{}

View File

@ -4,11 +4,16 @@ 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';
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 +24,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 +47,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 +62,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();
});
@ -74,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()) {
@ -91,8 +163,7 @@ export class MenuScene extends Phaser.Scene {
private closeSideMenu(): void {
if (!this.sideMenuOpened) return;
this.sideMenuOpened = false;
this.closeGameQualityMenu()
this.menuButton.getChildByID('openMenuButton').innerHTML = `<img src="/static/images/menu.svg">`;
this.closeAll();
gameManager.getCurrentGameScene(this).ConsoleGlobalMessageManager.disabledMessageConsole();
this.tweens.add({
targets: this.menuElement,
@ -102,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;
@ -158,7 +229,249 @@ export class MenuScene extends Phaser.Scene {
});
}
private openGameLogin(): void{
if (this.gameLoginMenuOpened) {
this.closeGameLogin();
return;
}
this.closeAll();
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;
}
//close all
this.closeAll();
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 HTMLParagraphElement;
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;}
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';
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
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(){
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{
//close all
this.closeAll();
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{
//close all
this.closeAll();
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 +492,7 @@ export class MenuScene extends Phaser.Scene {
this.closeSideMenu();
break;
case 'shareButton':
this.shareUrl();
this.openGameShare();
break;
case 'editGameSettingsButton':
this.openGameSettingsMenu();
@ -187,12 +500,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){
@ -213,4 +531,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 = `<img src="/static/images/menu.svg">`;
}
}

View File

@ -16,6 +16,10 @@ export class AuthenticateController extends BaseController {
this.register();
this.verify();
this.anonymLogin();
this.userRegister();
this.userLogin();
this.forgotPassword();
this.notificationRecent();
}
//Try to login with an admin token
@ -132,4 +136,150 @@ 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
}));
}
})();
});
}
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
}));
}
})();
});
}
}

View File

@ -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;

View File

@ -110,6 +110,68 @@ 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}`}
});
}
getNotification() {
return Axios.get(`${ADMIN_API_URL}/notifications/recent`,
{
headers: {"Authorization": `${ADMIN_API_TOKEN}`}
});
}
}
export const adminApi = new AdminApi();

View File

@ -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<string> {