Changes the prefix added in front of the jitsiRoomName
Previously, the prefix was computed using the org/world (in SAAS) or the instance part of public URLs. Neither was guaranteeing the Jitsi Room would be unique accross rooms. The new system computes a hash of the room URL and prepends it to the jitsi room name. BREAKING CHANGE: this means the URL of the Jitsi room will change for all maps. Users having bookmarked the Jitsi room (for instance in the Jitsi mobile app) will need to update their bookmarks.
This commit is contained in:
parent
184aeb354a
commit
b89997c9f1
@ -562,14 +562,13 @@ export class GameRoom {
|
|||||||
if (!ADMIN_API_URL) {
|
if (!ADMIN_API_URL) {
|
||||||
const roomUrlObj = new URL(roomUrl);
|
const roomUrlObj = new URL(roomUrl);
|
||||||
|
|
||||||
const match = /\/_\/([^/]+)\/(.+)/.exec(roomUrlObj.pathname);
|
const match = /\/_\/[^/]+\/(.+)/.exec(roomUrlObj.pathname);
|
||||||
if (!match) {
|
if (!match) {
|
||||||
console.error("Unexpected room URL", roomUrl);
|
console.error("Unexpected room URL", roomUrl);
|
||||||
throw new Error('Unexpected room URL "' + roomUrl + '"');
|
throw new Error('Unexpected room URL "' + roomUrl + '"');
|
||||||
}
|
}
|
||||||
|
|
||||||
const instance = match[1];
|
const mapUrl = roomUrlObj.protocol + "//" + match[1];
|
||||||
const mapUrl = roomUrlObj.protocol + "//" + match[2];
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
mapUrl,
|
mapUrl,
|
||||||
@ -579,7 +578,6 @@ export class GameRoom {
|
|||||||
roomSlug: null,
|
roomSlug: null,
|
||||||
contactPage: null,
|
contactPage: null,
|
||||||
group: null,
|
group: null,
|
||||||
instance,
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -18,7 +18,6 @@ export class Room {
|
|||||||
private _authenticationMandatory: boolean = DISABLE_ANONYMOUS;
|
private _authenticationMandatory: boolean = DISABLE_ANONYMOUS;
|
||||||
private _iframeAuthentication?: string = OPID_LOGIN_SCREEN_PROVIDER;
|
private _iframeAuthentication?: string = OPID_LOGIN_SCREEN_PROVIDER;
|
||||||
private _mapUrl: string | undefined;
|
private _mapUrl: string | undefined;
|
||||||
private _instance: string | undefined;
|
|
||||||
private readonly _search: URLSearchParams;
|
private readonly _search: URLSearchParams;
|
||||||
private _contactPage: string | undefined;
|
private _contactPage: string | undefined;
|
||||||
private _group: string | null = null;
|
private _group: string | null = null;
|
||||||
@ -121,7 +120,6 @@ export class Room {
|
|||||||
this._canReport = data.canReport ?? false;
|
this._canReport = data.canReport ?? false;
|
||||||
this._loadingLogo = data.loadingLogo ?? undefined;
|
this._loadingLogo = data.loadingLogo ?? undefined;
|
||||||
this._loginSceneLogo = data.loginSceneLogo ?? undefined;
|
this._loginSceneLogo = data.loginSceneLogo ?? undefined;
|
||||||
this._instance = data.instance;
|
|
||||||
return new MapDetail(data.mapUrl);
|
return new MapDetail(data.mapUrl);
|
||||||
} else {
|
} else {
|
||||||
console.log(data);
|
console.log(data);
|
||||||
@ -200,13 +198,6 @@ export class Room {
|
|||||||
return this._group;
|
return this._group;
|
||||||
}
|
}
|
||||||
|
|
||||||
get instance(): string {
|
|
||||||
if (!this._instance) {
|
|
||||||
throw new Error("Instance not fetched yet");
|
|
||||||
}
|
|
||||||
return this._instance;
|
|
||||||
}
|
|
||||||
|
|
||||||
get expireOn(): Date | undefined {
|
get expireOn(): Date | undefined {
|
||||||
return this._expireOn;
|
return this._expireOn;
|
||||||
}
|
}
|
||||||
|
@ -67,7 +67,7 @@ export class GameMapPropertiesListener {
|
|||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
const openJitsiRoomFunction = () => {
|
const openJitsiRoomFunction = () => {
|
||||||
const roomName = jitsiFactory.getRoomName(newValue.toString(), this.scene.instance);
|
const roomName = jitsiFactory.getRoomName(newValue.toString(), this.scene.roomUrl);
|
||||||
const jitsiUrl = allProps.get(GameMapProperties.JITSI_URL) as string | undefined;
|
const jitsiUrl = allProps.get(GameMapProperties.JITSI_URL) as string | undefined;
|
||||||
|
|
||||||
if (JITSI_PRIVATE_MODE && !jitsiUrl) {
|
if (JITSI_PRIVATE_MODE && !jitsiUrl) {
|
||||||
|
@ -185,7 +185,6 @@ export class GameScene extends DirtyScene {
|
|||||||
private biggestAvailableAreaStoreUnsubscribe!: () => void;
|
private biggestAvailableAreaStoreUnsubscribe!: () => void;
|
||||||
MapUrlFile: string;
|
MapUrlFile: string;
|
||||||
roomUrl: string;
|
roomUrl: string;
|
||||||
instance: string;
|
|
||||||
|
|
||||||
currentTick!: number;
|
currentTick!: number;
|
||||||
lastSentTick!: number; // The last tick at which a position was sent.
|
lastSentTick!: number; // The last tick at which a position was sent.
|
||||||
@ -234,7 +233,6 @@ export class GameScene extends DirtyScene {
|
|||||||
});
|
});
|
||||||
this.Terrains = [];
|
this.Terrains = [];
|
||||||
this.groups = new Map<number, Sprite>();
|
this.groups = new Map<number, Sprite>();
|
||||||
this.instance = room.instance;
|
|
||||||
|
|
||||||
this.MapUrlFile = MapUrlFile;
|
this.MapUrlFile = MapUrlFile;
|
||||||
this.roomUrl = room.key;
|
this.roomUrl = room.key;
|
||||||
|
@ -9,4 +9,21 @@ export class StringUtils {
|
|||||||
}
|
}
|
||||||
return { x: values[0], y: values[1] };
|
return { x: values[0], y: values[1] };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Computes a "short URL" hash of the string passed in parameter.
|
||||||
|
*/
|
||||||
|
public static shortHash = function (s: string): string {
|
||||||
|
let hash = 0;
|
||||||
|
const strLength = s.length;
|
||||||
|
if (strLength === 0) {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
for (let i = 0; i < strLength; i++) {
|
||||||
|
const c = s.charCodeAt(i);
|
||||||
|
hash = (hash << 5) - hash + c;
|
||||||
|
hash = hash & hash; // Convert to 32bit integer
|
||||||
|
}
|
||||||
|
return Math.abs(hash).toString(36);
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
@ -5,6 +5,7 @@ import { get } from "svelte/store";
|
|||||||
import CancelablePromise from "cancelable-promise";
|
import CancelablePromise from "cancelable-promise";
|
||||||
import { gameManager } from "../Phaser/Game/GameManager";
|
import { gameManager } from "../Phaser/Game/GameManager";
|
||||||
import { jitsiParticipantsCountStore, userIsJitsiDominantSpeakerStore } from "../Stores/GameStore";
|
import { jitsiParticipantsCountStore, userIsJitsiDominantSpeakerStore } from "../Stores/GameStore";
|
||||||
|
import { StringUtils } from "../Utils/StringUtils";
|
||||||
|
|
||||||
interface jitsiConfigInterface {
|
interface jitsiConfigInterface {
|
||||||
startWithAudioMuted: boolean;
|
startWithAudioMuted: boolean;
|
||||||
@ -120,7 +121,7 @@ const slugify = (...args: (string | number)[]): string => {
|
|||||||
.replace(/[\u0300-\u036f]/g, "") // remove all previously split accents
|
.replace(/[\u0300-\u036f]/g, "") // remove all previously split accents
|
||||||
.toLowerCase()
|
.toLowerCase()
|
||||||
.trim()
|
.trim()
|
||||||
.replace(/[^a-z0-9 ]/g, "") // remove all chars not letters, numbers and spaces (to be replaced)
|
.replace(/[^a-z0-9-_ ]/g, "") // remove all chars not letters, numbers, dash, underscores and spaces (to be replaced)
|
||||||
.replace(/\s+/g, "-"); // separator
|
.replace(/\s+/g, "-"); // separator
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -135,8 +136,8 @@ class JitsiFactory {
|
|||||||
/**
|
/**
|
||||||
* Slugifies the room name and prepends the room name with the instance
|
* Slugifies the room name and prepends the room name with the instance
|
||||||
*/
|
*/
|
||||||
public getRoomName(roomName: string, instance: string): string {
|
public getRoomName(roomName: string, roomId: string): string {
|
||||||
return slugify(instance.replace("/", "-") + "-" + roomName);
|
return slugify(StringUtils.shortHash(roomId) + "-" + roomName);
|
||||||
}
|
}
|
||||||
|
|
||||||
public start(
|
public start(
|
||||||
|
@ -13,7 +13,6 @@ export const isMapDetailsData = z.object({
|
|||||||
roomSlug: z.nullable(z.string()), // deprecated
|
roomSlug: z.nullable(z.string()), // deprecated
|
||||||
contactPage: z.nullable(z.string()),
|
contactPage: z.nullable(z.string()),
|
||||||
group: z.nullable(z.string()),
|
group: z.nullable(z.string()),
|
||||||
instance: z.string(),
|
|
||||||
|
|
||||||
iframeAuthentication: z.optional(z.nullable(z.string())),
|
iframeAuthentication: z.optional(z.nullable(z.string())),
|
||||||
// The date (in ISO 8601 format) at which the room will expire
|
// The date (in ISO 8601 format) at which the room will expire
|
||||||
|
@ -76,10 +76,6 @@ export class MapController extends BaseHttpController {
|
|||||||
* type: string|null
|
* type: string|null
|
||||||
* description: The group this room is part of (maps the notion of "world" in WorkAdventure SAAS)
|
* description: The group this room is part of (maps the notion of "world" in WorkAdventure SAAS)
|
||||||
* example: myorg/myworld
|
* example: myorg/myworld
|
||||||
* instance:
|
|
||||||
* type: string
|
|
||||||
* description: The instance of this map. In a public URL: the second part of the URL (_/[instance]/map.json)
|
|
||||||
* example: global
|
|
||||||
* iframeAuthentication:
|
* iframeAuthentication:
|
||||||
* type: string|null
|
* type: string|null
|
||||||
* description: The URL of the authentication Iframe
|
* description: The URL of the authentication Iframe
|
||||||
@ -115,15 +111,14 @@ export class MapController extends BaseHttpController {
|
|||||||
if (!ADMIN_API_URL) {
|
if (!ADMIN_API_URL) {
|
||||||
const roomUrl = new URL(query.playUri);
|
const roomUrl = new URL(query.playUri);
|
||||||
|
|
||||||
const match = /\/_\/([^/]+)\/(.+)/.exec(roomUrl.pathname);
|
const match = /\/_\/[^/]+\/(.+)/.exec(roomUrl.pathname);
|
||||||
if (!match) {
|
if (!match) {
|
||||||
res.status(404);
|
res.status(404);
|
||||||
res.json({});
|
res.json({});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const instance = match[1];
|
const mapUrl = roomUrl.protocol + "//" + match[1];
|
||||||
const mapUrl = roomUrl.protocol + "//" + match[2];
|
|
||||||
|
|
||||||
res.json({
|
res.json({
|
||||||
mapUrl,
|
mapUrl,
|
||||||
@ -133,7 +128,6 @@ export class MapController extends BaseHttpController {
|
|||||||
tags: [],
|
tags: [],
|
||||||
contactPage: null,
|
contactPage: null,
|
||||||
authenticationMandatory: DISABLE_ANONYMOUS,
|
authenticationMandatory: DISABLE_ANONYMOUS,
|
||||||
instance,
|
|
||||||
} as MapDetailsData);
|
} as MapDetailsData);
|
||||||
|
|
||||||
return;
|
return;
|
||||||
|
Loading…
Reference in New Issue
Block a user