backend: use env var MEET_BASE_URL to set base URL in moderatorUrl and speakerUrl of room objects dynamically instead of storing it
This commit is contained in:
parent
fa1582bee0
commit
0abbaddaf9
@ -3,6 +3,7 @@ import { Request, Response } from 'express';
|
||||
import { Readable } from 'stream';
|
||||
import { container } from '../config/index.js';
|
||||
import INTERNAL_CONFIG from '../config/internal-config.js';
|
||||
import { getBaseUrl } from '../environment.js';
|
||||
import { RecordingHelper } from '../helpers/index.js';
|
||||
import {
|
||||
errorRecordingNotFound,
|
||||
@ -23,7 +24,7 @@ export const startRecording = async (req: Request, res: Response) => {
|
||||
const recordingInfo = await recordingService.startRecording(roomId);
|
||||
res.setHeader(
|
||||
'Location',
|
||||
`${req.protocol}://${req.get('host')}${INTERNAL_CONFIG.API_BASE_PATH_V1}/recordings/${recordingInfo.recordingId}`
|
||||
`${getBaseUrl()}${INTERNAL_CONFIG.API_BASE_PATH_V1}/recordings/${recordingInfo.recordingId}`
|
||||
);
|
||||
|
||||
return res.status(201).json(recordingInfo);
|
||||
@ -126,7 +127,7 @@ export const stopRecording = async (req: Request, res: Response) => {
|
||||
const recordingInfo = await recordingService.stopRecording(recordingId);
|
||||
res.setHeader(
|
||||
'Location',
|
||||
`${req.protocol}://${req.get('host')}${INTERNAL_CONFIG.API_BASE_PATH_V1}/recordings/${recordingId}`
|
||||
`${getBaseUrl()}${INTERNAL_CONFIG.API_BASE_PATH_V1}/recordings/${recordingId}`
|
||||
);
|
||||
return res.status(202).json(recordingInfo);
|
||||
} catch (error) {
|
||||
@ -250,7 +251,7 @@ export const getRecordingUrl = async (req: Request, res: Response) => {
|
||||
}
|
||||
|
||||
const secret = privateAccess ? recordingSecrets.privateAccessSecret : recordingSecrets.publicAccessSecret;
|
||||
const recordingUrl = `${req.protocol}://${req.get('host')}/recording/${recordingId}?secret=${secret}`;
|
||||
const recordingUrl = `${getBaseUrl()}/recording/${recordingId}?secret=${secret}`;
|
||||
|
||||
return res.status(200).json({ url: recordingUrl });
|
||||
} catch (error) {
|
||||
|
||||
@ -10,6 +10,7 @@ import {
|
||||
import { Request, Response } from 'express';
|
||||
import { container } from '../config/index.js';
|
||||
import INTERNAL_CONFIG from '../config/internal-config.js';
|
||||
import { getBaseUrl } from '../environment.js';
|
||||
import { handleError } from '../models/error.model.js';
|
||||
import { LoggerService, ParticipantService, RoomService } from '../services/index.js';
|
||||
import { getCookieOptions } from '../utils/cookie-utils.js';
|
||||
@ -21,10 +22,9 @@ export const createRoom = async (req: Request, res: Response) => {
|
||||
|
||||
try {
|
||||
logger.verbose(`Creating room with options '${JSON.stringify(options)}'`);
|
||||
const baseUrl = `${req.protocol}://${req.get('host')}`;
|
||||
|
||||
const room = await roomService.createMeetRoom(baseUrl, options);
|
||||
res.set('Location', `${baseUrl}${INTERNAL_CONFIG.API_BASE_PATH_V1}/rooms/${room.roomId}`);
|
||||
const room = await roomService.createMeetRoom(options);
|
||||
res.set('Location', `${getBaseUrl()}${INTERNAL_CONFIG.API_BASE_PATH_V1}/rooms/${room.roomId}`);
|
||||
return res.status(201).json(room);
|
||||
} catch (error) {
|
||||
handleError(res, error, 'creating room');
|
||||
|
||||
@ -20,6 +20,7 @@ export const {
|
||||
SERVER_CORS_ORIGIN = '*',
|
||||
MEET_LOG_LEVEL = 'info',
|
||||
MEET_NAME_ID = 'openviduMeet',
|
||||
MEET_BASE_URL = `http://localhost:${SERVER_PORT}`,
|
||||
|
||||
/**
|
||||
* Authentication configuration
|
||||
@ -85,6 +86,13 @@ export const {
|
||||
ENABLED_MODULES = ''
|
||||
} = process.env;
|
||||
|
||||
/**
|
||||
* Gets the base URL without trailing slash
|
||||
*/
|
||||
export const getBaseUrl = (): string => {
|
||||
return MEET_BASE_URL.endsWith('/') ? MEET_BASE_URL.slice(0, -1) : MEET_BASE_URL;
|
||||
};
|
||||
|
||||
export function checkModuleEnabled() {
|
||||
if (MODULES_FILE) {
|
||||
const moduleName = MODULE_NAME;
|
||||
|
||||
@ -72,14 +72,13 @@ export class RoomService {
|
||||
/**
|
||||
* Creates an OpenVidu Meet room with the specified options.
|
||||
*
|
||||
* @param {string} baseUrl - The base URL for the room.
|
||||
* @param {MeetRoomOptions} options - The options for creating the OpenVidu room.
|
||||
* @returns {Promise<MeetRoom>} A promise that resolves to the created OpenVidu room.
|
||||
*
|
||||
* @throws {Error} If the room creation fails.
|
||||
*
|
||||
*/
|
||||
async createMeetRoom(baseUrl: string, roomOptions: MeetRoomOptions): Promise<MeetRoom> {
|
||||
async createMeetRoom(roomOptions: MeetRoomOptions): Promise<MeetRoom> {
|
||||
const { roomName, autoDeletionDate, autoDeletionPolicy, config } = roomOptions;
|
||||
const roomIdPrefix = roomName!.replace(/\s+/g, ''); // Remove all spaces
|
||||
const roomId = `${roomIdPrefix}-${uid(15)}`; // Generate a unique room ID based on the room name
|
||||
@ -92,14 +91,12 @@ export class RoomService {
|
||||
autoDeletionDate,
|
||||
autoDeletionPolicy,
|
||||
config: config!,
|
||||
moderatorUrl: `${baseUrl}/room/${roomId}?secret=${secureUid(10)}`,
|
||||
speakerUrl: `${baseUrl}/room/${roomId}?secret=${secureUid(10)}`,
|
||||
moderatorUrl: `/room/${roomId}?secret=${secureUid(10)}`,
|
||||
speakerUrl: `/room/${roomId}?secret=${secureUid(10)}`,
|
||||
status: MeetRoomStatus.OPEN,
|
||||
meetingEndAction: MeetingEndAction.NONE
|
||||
};
|
||||
|
||||
await this.storageService.saveMeetRoom(meetRoom);
|
||||
return meetRoom;
|
||||
return await this.storageService.saveMeetRoom(meetRoom);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@ -3,6 +3,7 @@ import { inject, injectable } from 'inversify';
|
||||
import ms from 'ms';
|
||||
import { Readable } from 'stream';
|
||||
import {
|
||||
getBaseUrl,
|
||||
MEET_INITIAL_ADMIN_PASSWORD,
|
||||
MEET_INITIAL_ADMIN_USER,
|
||||
MEET_INITIAL_API_KEY,
|
||||
@ -168,7 +169,12 @@ export class MeetStorageService<
|
||||
const redisKey = RedisKeyName.ROOM + roomId;
|
||||
const storageKey = this.keyBuilder.buildMeetRoomKey(roomId);
|
||||
|
||||
return await this.saveCacheAndStorage<MRoom>(redisKey, storageKey, meetRoom);
|
||||
// Normalize room data for storage (ensure only paths are stored)
|
||||
const normalizedRoom = this.normalizeRoomForStorage(meetRoom);
|
||||
await this.saveCacheAndStorage<MRoom>(redisKey, storageKey, normalizedRoom);
|
||||
|
||||
// Return room with full URLs
|
||||
return this.enrichRoomWithBaseUrls(normalizedRoom);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -206,7 +212,13 @@ export class MeetStorageService<
|
||||
if (item.Key && item.Key.endsWith('.json')) {
|
||||
try {
|
||||
const room = await this.storageProvider.getObject<MRoom>(item.Key);
|
||||
return room;
|
||||
|
||||
if (!room) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// Add base URL to moderator and speaker URLs
|
||||
return this.enrichRoomWithBaseUrls(room);
|
||||
} catch (error) {
|
||||
this.logger.warn(`Failed to load room from ${item.Key}: ${error}`);
|
||||
return null;
|
||||
@ -235,7 +247,14 @@ export class MeetStorageService<
|
||||
const redisKey = RedisKeyName.ROOM + roomId;
|
||||
const storageKey = this.keyBuilder.buildMeetRoomKey(roomId);
|
||||
|
||||
return await this.getFromCacheAndStorage<MRoom>(redisKey, storageKey);
|
||||
const room = await this.getFromCacheAndStorage<MRoom>(redisKey, storageKey);
|
||||
|
||||
if (!room) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// Add base URL to moderator and speaker URLs
|
||||
return this.enrichRoomWithBaseUrls(room);
|
||||
}
|
||||
|
||||
async deleteMeetRooms(roomIds: string[]): Promise<void> {
|
||||
@ -253,7 +272,14 @@ export class MeetStorageService<
|
||||
const redisKey = RedisKeyName.ARCHIVED_ROOM + roomId;
|
||||
const storageKey = this.keyBuilder.buildArchivedMeetRoomKey(roomId);
|
||||
|
||||
return await this.getFromCacheAndStorage<Partial<MRoom>>(redisKey, storageKey);
|
||||
const archivedRoom = await this.getFromCacheAndStorage<Partial<MRoom>>(redisKey, storageKey);
|
||||
|
||||
if (!archivedRoom) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// Add base URL to moderator and speaker URLs
|
||||
return this.enrichRoomWithBaseUrls(archivedRoom as MRoom);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -298,7 +324,9 @@ export class MeetStorageService<
|
||||
}
|
||||
} as Partial<MRoom>;
|
||||
|
||||
await this.saveCacheAndStorage<Partial<MRoom>>(redisKey, storageKey, archivedRoom);
|
||||
// Normalize room data for storage (ensure only paths are stored)
|
||||
const normalizedRoom = this.normalizeRoomForStorage(archivedRoom as MRoom);
|
||||
await this.saveCacheAndStorage<Partial<MRoom>>(redisKey, storageKey, normalizedRoom);
|
||||
}
|
||||
|
||||
async deleteArchivedRoomMetadata(roomId: string): Promise<void> {
|
||||
@ -731,6 +759,53 @@ export class MeetStorageService<
|
||||
this.logger.info('API key initialized');
|
||||
}
|
||||
|
||||
/**
|
||||
* Normalizes room data for storage by ensuring URLs contain only paths
|
||||
* @param room - The room object to normalize
|
||||
* @returns The room object with path-only URLs for storage
|
||||
*/
|
||||
private normalizeRoomForStorage(room: MRoom): MRoom {
|
||||
return {
|
||||
...room,
|
||||
moderatorUrl: this.extractPathFromUrl(room.moderatorUrl),
|
||||
speakerUrl: this.extractPathFromUrl(room.speakerUrl)
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Extracts path from URL, handling both full URLs and path-only strings
|
||||
* @param url - The URL or path to process
|
||||
* @returns The path portion of the URL
|
||||
*/
|
||||
private extractPathFromUrl(url: string): string {
|
||||
// If it's already a path (starts with /), return as-is
|
||||
if (url.startsWith('/')) {
|
||||
return url;
|
||||
}
|
||||
|
||||
// If it's a full URL, extract the path
|
||||
try {
|
||||
const urlObj = new URL(url);
|
||||
return urlObj.pathname + urlObj.search + urlObj.hash;
|
||||
} catch (error) {
|
||||
this.logger.warn(`Failed to parse URL for path extraction: ${url}. Treating as path.`);
|
||||
return url;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Enriches a room object with base URLs for moderator and speaker URLs
|
||||
* @param room - The room object to enrich
|
||||
* @returns The room object with full URLs
|
||||
*/
|
||||
protected enrichRoomWithBaseUrls(room: MRoom): MRoom {
|
||||
return {
|
||||
...room,
|
||||
moderatorUrl: `${getBaseUrl()}${room.moderatorUrl}`,
|
||||
speakerUrl: `${getBaseUrl()}${room.speakerUrl}`
|
||||
};
|
||||
}
|
||||
|
||||
protected async getRecordingFileSize(key: string, recordingId: string): Promise<number> {
|
||||
const { contentLength: fileSize } = await this.storageProvider.getObjectHeaders(key);
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user