workadventure/front/src/Connexion/ConnectionManager.ts
David Négrier c9fa9b9a92 Migrating away from the notion of public/private URL in WorkAdventure Github repository
The notion of public/private repositories (with /_/ and /@/ URLs) is specific to the SAAS version of WorkAdventure.
It would be better to avoid leaking the organization/world/room structure of the private SAAS URLs inside the WorkAdventure Github project.

Rather than sending http://admin_host/api/map?organizationSlug=...&worldSlug=...&roomSlug=...., we are now sending /api/map&playUri=...
where playUri is the full URL of the current game.
This allows the backend to act as a complete router.
The front (and the pusher) will be able to completely ignore the specifics of URL building (with /@/ and /_/ URLs, etc...)
Those details will live only in the admin server, which is way cleaner (and way more powerful).
2021-07-15 17:07:47 +02:00

149 lines
6.6 KiB
TypeScript

import Axios from "axios";
import {PUSHER_URL, START_ROOM_URL} from "../Enum/EnvironmentVariable";
import {RoomConnection} from "./RoomConnection";
import type {OnConnectInterface, PositionInterface, ViewportInterface} from "./ConnexionModels";
import {GameConnexionTypes, urlManager} from "../Url/UrlManager";
import {localUserStore} from "./LocalUserStore";
import {CharacterTexture, LocalUser} from "./LocalUser";
import {Room} from "./Room";
class ConnectionManager {
private localUser!:LocalUser;
private connexionType?: GameConnexionTypes
private reconnectingTimeout: NodeJS.Timeout|null = null;
private _unloading:boolean = false;
get unloading () {
return this._unloading;
}
constructor() {
window.addEventListener('beforeunload', () => {
this._unloading = true;
if (this.reconnectingTimeout) clearTimeout(this.reconnectingTimeout)
})
}
/**
* Tries to login to the node server and return the starting map url to be loaded
*/
public async initGameConnexion(): Promise<Room> {
const connexionType = urlManager.getGameConnexionType();
this.connexionType = connexionType;
if(connexionType === GameConnexionTypes.register) {
const organizationMemberToken = urlManager.getOrganizationToken();
const data = await Axios.post(`${PUSHER_URL}/register`, {organizationMemberToken}).then(res => res.data);
this.localUser = new LocalUser(data.userUuid, data.authToken, data.textures);
localUserStore.saveUser(this.localUser);
const roomUrl = data.roomUrl;
const room = await Room.createRoom(new URL(window.location.protocol + '//' + window.location.host + roomUrl + window.location.search + window.location.hash));
urlManager.pushRoomIdToUrl(room);
return Promise.resolve(room);
} else if (connexionType === GameConnexionTypes.organization || connexionType === GameConnexionTypes.anonymous || connexionType === GameConnexionTypes.empty) {
let localUser = localUserStore.getLocalUser();
if (localUser && localUser.jwtToken && localUser.uuid && localUser.textures) {
this.localUser = localUser;
try {
await this.verifyToken(localUser.jwtToken);
} catch(e) {
// If the token is invalid, let's generate an anonymous one.
console.error('JWT token invalid. Did it expire? Login anonymously instead.');
await this.anonymousLogin();
}
}else{
await this.anonymousLogin();
}
localUser = localUserStore.getLocalUser();
if(!localUser){
throw "Error to store local user data";
}
let roomPath: string;
if (connexionType === GameConnexionTypes.empty) {
roomPath = window.location.protocol + '//' + window.location.host + START_ROOM_URL;
} else {
roomPath = window.location.protocol + '//' + window.location.host + window.location.pathname + window.location.search + window.location.hash;
}
//get detail map for anonymous login and set texture in local storage
const room = await Room.createRoom(new URL(roomPath));
if(room.textures != undefined && room.textures.length > 0) {
//check if texture was changed
if(localUser.textures.length === 0){
localUser.textures = room.textures;
}else{
room.textures.forEach((newTexture) => {
const alreadyExistTexture = localUser?.textures.find((c) => newTexture.id === c.id);
if(localUser?.textures.findIndex((c) => newTexture.id === c.id) !== -1){
return;
}
localUser?.textures.push(newTexture)
});
}
this.localUser = localUser;
localUserStore.saveUser(localUser);
}
return Promise.resolve(room);
}
return Promise.reject(new Error('Invalid URL'));
}
private async verifyToken(token: string): Promise<void> {
await Axios.get(`${PUSHER_URL}/verify`, {params: {token}});
}
public async anonymousLogin(isBenchmark: boolean = false): Promise<void> {
const data = await Axios.post(`${PUSHER_URL}/anonymLogin`).then(res => res.data);
this.localUser = new LocalUser(data.userUuid, data.authToken, []);
if (!isBenchmark) { // In benchmark, we don't have a local storage.
localUserStore.saveUser(this.localUser);
}
}
public initBenchmark(): void {
this.localUser = new LocalUser('', 'test', []);
}
public connectToRoomSocket(roomUrl: string, name: string, characterLayers: string[], position: PositionInterface, viewport: ViewportInterface, companion: string|null): Promise<OnConnectInterface> {
return new Promise<OnConnectInterface>((resolve, reject) => {
const connection = new RoomConnection(this.localUser.jwtToken, roomUrl, name, characterLayers, position, viewport, companion);
connection.onConnectError((error: object) => {
console.log('An error occurred while connecting to socket server. Retrying');
reject(error);
});
connection.onConnectingError((event: CloseEvent) => {
console.log('An error occurred while connecting to socket server. Retrying');
reject(new Error('An error occurred while connecting to socket server. Retrying. Code: '+event.code+', Reason: '+event.reason));
});
connection.onConnect((connect: OnConnectInterface) => {
resolve(connect);
});
}).catch((err) => {
// Let's retry in 4-6 seconds
return new Promise<OnConnectInterface>((resolve, reject) => {
this.reconnectingTimeout = setTimeout(() => {
//todo: allow a way to break recursion?
//todo: find a way to avoid recursive function. Otherwise, the call stack will grow indefinitely.
this.connectToRoomSocket(roomUrl, name, characterLayers, position, viewport, companion).then((connection) => resolve(connection));
}, 4000 + Math.floor(Math.random() * 2000) );
});
});
}
get getConnexionType(){
return this.connexionType;
}
}
export const connectionManager = new ConnectionManager();