partey_workadventure/front/src/Connexion/Room.ts

253 lines
8.4 KiB
TypeScript
Raw Normal View History

import { CONTACT_URL, PUSHER_URL, DISABLE_ANONYMOUS, OPID_LOGIN_SCREEN_PROVIDER } from "../Enum/EnvironmentVariable";
import { localUserStore } from "./LocalUserStore";
import axios from "axios";
2021-12-06 18:20:06 +01:00
import { axiosWithRetry } from "./AxiosUtils";
import { isMapDetailsData } from "../Messages/JsonMessages/MapDetailsData";
import { isRoomRedirect } from "../Messages/JsonMessages/RoomRedirect";
2021-06-23 12:42:24 +02:00
export class MapDetail {
Refactoring Woka management (#1810) * Wrap websockets with HyperExpress * Add endpoints on pusher to resolve wokas * getting textures urls from pusher * Adding OpenAPI documentation for the pusher. The pusher now exposes a "/openapi" endpoint and a "/swagger-ui/" endpoint. * revert FRONT_URL * playerTextures metadata is being loaded via Phaser.Loader * fetch textures every time character or customize scene is open * Heavy changes: refactoring the pusher to always send the textures (and the front to accept them) * Sending character layer details to admin * Cleaning commented code * Fixing regex * Fix woka endpoints on pusher * Change error wording on pusher * Working on integration of the woka-list with the new admin endpoint. * Switching from "name" to "id" in texture object + using zod for woka/list validation * Add position on default woka data * Remove async on pusher option method * Fix woka list url * add options for /register * Fxiing loading the Woka list * Actually returning something in logout-callback * Copying messages to back too * remove customize button if no body parts are available (#1952) * remove customize button if no body parts are available * remove unused position field from PlayerTexturesCollection interface * removed unused label field * fix LocalUser test * little PlayerTextures class refactor * Fixing linting * Fixing missing Openapi packages in prod * Fixing back build Co-authored-by: Hanusiak Piotr <piotr@ltmp.co> Co-authored-by: David Négrier <d.negrier@thecodingmachine.com> * Add returns on pusher endpoints Co-authored-by: Alexis Faizeau <a.faizeau@workadventu.re> Co-authored-by: Hanusiak Piotr <piotr@ltmp.co> Co-authored-by: Piotr Hanusiak <wacneg@gmail.com>
2022-03-11 17:02:58 +01:00
constructor(public readonly mapUrl: string) {}
}
export interface RoomRedirect {
redirectUrl: string;
}
export class Room {
public readonly id: string;
/**
* @deprecated
*/
private readonly isPublic: boolean;
private _authenticationMandatory: boolean = DISABLE_ANONYMOUS;
private _iframeAuthentication?: string = OPID_LOGIN_SCREEN_PROVIDER;
private _mapUrl: string | undefined;
2021-06-23 12:42:24 +02:00
private instance: string | undefined;
private readonly _search: URLSearchParams;
private _contactPage: string | undefined;
private _group: string | null = null;
private _expireOn: Date | undefined;
private _canReport: boolean = false;
private _loadingLogo: string | undefined;
private _loginSceneLogo: string | undefined;
private constructor(private roomUrl: URL) {
this.id = roomUrl.pathname;
if (this.id.startsWith("/")) {
this.id = this.id.substr(1);
}
if (this.id.startsWith("_/") || this.id.startsWith("*/")) {
this.isPublic = true;
} else if (this.id.startsWith("@/")) {
this.isPublic = false;
} else {
throw new Error("Invalid room ID");
}
this._search = new URLSearchParams(roomUrl.search);
}
/**
* Creates a "Room" object representing the room.
* This method will follow room redirects if necessary, so the instance returned is a "real" room.
*/
public static async createRoom(roomUrl: URL): Promise<Room> {
let redirectCount = 0;
while (redirectCount < 32) {
const room = new Room(roomUrl);
const result = await room.getMapDetail();
if (result instanceof MapDetail) {
return room;
}
redirectCount++;
roomUrl = new URL(result.redirectUrl);
}
throw new Error("Room resolving seems stuck in a redirect loop after 32 redirect attempts");
}
public static getRoomPathFromExitUrl(exitUrl: string, currentRoomUrl: string): URL {
const url = new URL(exitUrl, currentRoomUrl);
return url;
}
/**
* @deprecated USage of exitSceneUrl is deprecated and therefore, this method is deprecated too.
*/
public static getRoomPathFromExitSceneUrl(
exitSceneUrl: string,
currentRoomUrl: string,
currentMapUrl: string
): URL {
const absoluteExitSceneUrl = new URL(exitSceneUrl, currentMapUrl);
const baseUrl = new URL(currentRoomUrl);
const currentRoom = new Room(baseUrl);
let instance: string = "global";
if (currentRoom.isPublic) {
instance = currentRoom.getInstance();
}
baseUrl.pathname = "/_/" + instance + "/" + absoluteExitSceneUrl.host + absoluteExitSceneUrl.pathname;
2022-01-09 13:51:44 +01:00
baseUrl.hash = absoluteExitSceneUrl.hash;
return baseUrl;
}
private async getMapDetail(): Promise<MapDetail | RoomRedirect> {
try {
const result = await axiosWithRetry.get(`${PUSHER_URL}/map`, {
params: {
playUri: this.roomUrl.toString(),
authToken: localUserStore.getAuthToken(),
},
});
const data = result.data;
if (data.authenticationMandatory !== undefined) {
data.authenticationMandatory = Boolean(data.authenticationMandatory);
}
const roomRedirectChecking = isRoomRedirect.safeParse(data);
const mapDetailsDataChecking = isMapDetailsData.safeParse(data);
if (roomRedirectChecking.success) {
return {
redirectUrl: data.redirectUrl,
};
} else if (mapDetailsDataChecking.success) {
console.log("Map ", this.id, " resolves to URL ", data.mapUrl);
this._mapUrl = data.mapUrl;
this._group = data.group;
this._authenticationMandatory =
data.authenticationMandatory != null ? data.authenticationMandatory : DISABLE_ANONYMOUS;
this._iframeAuthentication = data.iframeAuthentication || OPID_LOGIN_SCREEN_PROVIDER;
this._contactPage = data.contactPage || CONTACT_URL;
if (data.expireOn) {
this._expireOn = new Date(data.expireOn);
}
this._canReport = data.canReport ?? false;
this._loadingLogo = data.loadingLogo ?? undefined;
this._loginSceneLogo = data.loginSceneLogo ?? undefined;
this.instance = data.instance;
Refactoring Woka management (#1810) * Wrap websockets with HyperExpress * Add endpoints on pusher to resolve wokas * getting textures urls from pusher * Adding OpenAPI documentation for the pusher. The pusher now exposes a "/openapi" endpoint and a "/swagger-ui/" endpoint. * revert FRONT_URL * playerTextures metadata is being loaded via Phaser.Loader * fetch textures every time character or customize scene is open * Heavy changes: refactoring the pusher to always send the textures (and the front to accept them) * Sending character layer details to admin * Cleaning commented code * Fixing regex * Fix woka endpoints on pusher * Change error wording on pusher * Working on integration of the woka-list with the new admin endpoint. * Switching from "name" to "id" in texture object + using zod for woka/list validation * Add position on default woka data * Remove async on pusher option method * Fix woka list url * add options for /register * Fxiing loading the Woka list * Actually returning something in logout-callback * Copying messages to back too * remove customize button if no body parts are available (#1952) * remove customize button if no body parts are available * remove unused position field from PlayerTexturesCollection interface * removed unused label field * fix LocalUser test * little PlayerTextures class refactor * Fixing linting * Fixing missing Openapi packages in prod * Fixing back build Co-authored-by: Hanusiak Piotr <piotr@ltmp.co> Co-authored-by: David Négrier <d.negrier@thecodingmachine.com> * Add returns on pusher endpoints Co-authored-by: Alexis Faizeau <a.faizeau@workadventu.re> Co-authored-by: Hanusiak Piotr <piotr@ltmp.co> Co-authored-by: Piotr Hanusiak <wacneg@gmail.com>
2022-03-11 17:02:58 +01:00
return new MapDetail(data.mapUrl);
} else {
console.log(data);
console.error("roomRedirectChecking", roomRedirectChecking.error.issues);
console.error("mapDetailsDataChecking", mapDetailsDataChecking.error.issues);
throw new Error("Data received by the /map endpoint of the Pusher is not in a valid format.");
}
} catch (e) {
if (axios.isAxiosError(e) && e.response?.status == 401 && e.response?.data === "Token decrypted error") {
console.warn("JWT token sent could not be decrypted. Maybe it expired?");
localUserStore.setAuthToken(null);
window.location.assign("/login");
} else if (axios.isAxiosError(e)) {
console.error("Error => getMapDetail", e, e.response);
} else {
console.error("Error => getMapDetail", e);
}
throw e;
}
}
2020-10-13 18:44:50 +02:00
/**
* Instance name is:
* - In a public URL: the second part of the URL ( _/[instance]/map.json)
* - In a private URL: [organizationId/worldId]
*
* @deprecated
2020-10-13 18:44:50 +02:00
*/
public getInstance(): string {
if (this.instance !== undefined) {
return this.instance;
}
if (this.isPublic) {
const match = /[_*]\/([^/]+)\/.+/.exec(this.id);
2021-06-23 12:42:24 +02:00
if (!match) throw new Error('Could not extract instance from "' + this.id + '"');
2020-10-13 18:44:50 +02:00
this.instance = match[1];
return this.instance;
} else {
const match = /@\/([^/]+)\/([^/]+)\/.+/.exec(this.id);
2021-06-23 12:42:24 +02:00
if (!match) throw new Error('Could not extract instance from "' + this.id + '"');
this.instance = match[1] + "/" + match[2];
2020-10-13 18:44:50 +02:00
return this.instance;
}
}
2021-06-23 12:42:24 +02:00
public isDisconnected(): boolean {
const alone = this._search.get("alone");
if (alone && alone !== "0" && alone.toLowerCase() !== "false") {
return true;
}
return false;
}
public get search(): URLSearchParams {
return this._search;
}
/**
* 2 rooms are equal if they share the same path (but not necessarily the same hash)
* @param room
*/
public isEqual(room: Room): boolean {
return room.key === this.key;
}
/**
* A key representing this room
*/
public get key(): string {
const newUrl = new URL(this.roomUrl.toString());
newUrl.search = "";
newUrl.hash = "";
return newUrl.toString();
}
public get href(): string {
return this.roomUrl.toString();
}
get mapUrl(): string {
if (!this._mapUrl) {
throw new Error("Map URL not fetched yet");
}
return this._mapUrl;
}
Active authentication Oauth (#1377) * Active authentication Oauth - Google authentication - GitHub authentication - Linkedin authentication Signed-off-by: Gregoire Parant <g.parant@thecodingmachine.com> * Finish connexion et get user info connexion Signed-off-by: Gregoire Parant <g.parant@thecodingmachine.com> * Fix lint error Signed-off-by: Gregoire Parant <g.parant@thecodingmachine.com> * Change the expires token for 30 days Signed-off-by: Gregoire Parant <g.parant@thecodingmachine.com> * Update connexion stratgey - Set last room when it will be created and not when connexion is openned - Add '/login' end point permit to logout and open iframe to log user - Add logout feature permit to logout in front Signed-off-by: Gregoire Parant <g.parant@thecodingmachine.com> * Implement logout and revoke token with hydra Signed-off-by: Gregoire Parant <g.parant@thecodingmachine.com> * Fix pull develop conflict Signed-off-by: Gregoire Parant <g.parant@thecodingmachine.com> * Profile url (#1399) * Create function that permit to get profile URL Signed-off-by: Gregoire Parant <g.parant@thecodingmachine.com> * Continue profil user Signed-off-by: Gregoire Parant <g.parant@thecodingmachine.com> * Add menu and logout button Signed-off-by: Gregoire Parant <g.parant@thecodingmachine.com> * Update last room use Signed-off-by: Gregoire Parant <g.parant@thecodingmachine.com> * Profile callback permit to get url profile setting from admin Signed-off-by: Gregoire Parant <g.parant@thecodingmachine.com> * Finish profile show Signed-off-by: Gregoire Parant <g.parant@thecodingmachine.com> * Delete profileUrl will be not use today Signed-off-by: Gregoire Parant <g.parant@thecodingmachine.com> * Correct lint Signed-off-by: Gregoire Parant <g.parant@thecodingmachine.com> * Update size of iframe Signed-off-by: Gregoire Parant <g.parant@thecodingmachine.com> * Delete console log Signed-off-by: Gregoire Parant <g.parant@thecodingmachine.com> * Update feedback ARP Signed-off-by: Gregoire Parant <g.parant@thecodingmachine.com>
2021-09-05 18:17:49 +02:00
get authenticationMandatory(): boolean {
return this._authenticationMandatory;
}
get iframeAuthentication(): string | undefined {
return this._iframeAuthentication;
}
get contactPage(): string | undefined {
return this._contactPage;
}
get group(): string | null {
return this._group;
}
get expireOn(): Date | undefined {
return this._expireOn;
}
get canReport(): boolean {
return this._canReport;
}
get loadingLogo(): string | undefined {
return this._loadingLogo;
}
get loginSceneLogo(): string | undefined {
return this._loginSceneLogo;
}
}