From 6a8bae5dec1e27c517d17317bd7b18cf47f3f4a8 Mon Sep 17 00:00:00 2001 From: Carlos Santos <4a.santos@gmail.com> Date: Mon, 12 May 2025 12:57:33 +0200 Subject: [PATCH] testapp: enhance webhook event handling and storage; update room ID handling in forms --- testapp/package.json | 2 +- testapp/public/ts/webcomponent.ts | 219 +++++++++++++--------- testapp/public/views/index.mustache | 12 +- testapp/public/views/room.mustache | 22 ++- testapp/src/controllers/roomController.ts | 3 + testapp/src/index.ts | 2 +- 6 files changed, 168 insertions(+), 92 deletions(-) diff --git a/testapp/package.json b/testapp/package.json index 73ab3f3..4760397 100644 --- a/testapp/package.json +++ b/testapp/package.json @@ -7,7 +7,7 @@ "build": "tsc", "build:watch": "tsc --watch", "start": "node dist/index.js", - "dev:server": "ts-node-dev --respawn --watch src,public/ts src/index.ts", + "dev:server": "ts-node-dev --respawn --watch src,public/ts,public/views src/index.ts", "dev": "concurrently \"npm:watch:client\" \"npm run dev:server\" --kill-others", "build:client": "tsc -p tsconfig.client.json", "watch:client": "tsc -p tsconfig.client.json --watch" diff --git a/testapp/public/ts/webcomponent.ts b/testapp/public/ts/webcomponent.ts index 6b0f062..bf2e7d1 100644 --- a/testapp/public/ts/webcomponent.ts +++ b/testapp/public/ts/webcomponent.ts @@ -7,6 +7,7 @@ const addEventToLog = (eventType: string, eventMessage: string): void => { const eventsList = document.getElementById('events-list'); if (eventsList) { const li = document.createElement('li'); + li.className = `event-${eventType}`; li.textContent = `[ ${eventType} ] : ${eventMessage}`; eventsList.appendChild(li); } @@ -19,97 +20,135 @@ const escapeHtml = (unsafe: string): string => { .replace(/"/g, '"') .replace(/'/g, '''); }; + +const getWebhookEventsFromStorage = (roomId: string): any[] => { + const data = localStorage.getItem('webhookEventsByRoom'); + if (!data) return []; + const map = JSON.parse(data); + return map[roomId] || []; +}; + +const saveWebhookEventToStorage = (roomId: string, event: any): void => { + const data = localStorage.getItem('webhookEventsByRoom'); + const map = data ? JSON.parse(data) : {}; + if (!map[roomId]) map[roomId] = []; + map[roomId].push(event); + localStorage.setItem('webhookEventsByRoom', JSON.stringify(map)); +}; + +const clearWebhookEventsByRoom = (roomId: string): void => { + const data = localStorage.getItem('webhookEventsByRoom'); + if (!data) return; + const map = JSON.parse(data); + if (map[roomId]) { + map[roomId] = []; + localStorage.setItem('webhookEventsByRoom', JSON.stringify(map)); + } +}; + const listenWebhookServerEvents = () => { - socket.on('webhookEvent', (payload: any) => { - console.log('Webhook received:', payload); - const webhookLogList = document.getElementById('webhook-log-list'); - if (webhookLogList) { - // Create unique IDs for this accordion item - const itemId = payload.creationDate; - const headerId = `header-${itemId}`; - const collapseId = `collapse-${itemId}`; - - // Create accordion item container - const accordionItem = document.createElement('div'); - accordionItem.className = 'accordion-item'; - - // Create header - const header = document.createElement('h2'); - header.className = 'accordion-header'; - header.id = headerId; - - // Create header button - const button = document.createElement('button'); - button.className = 'accordion-button'; - button.type = 'button'; - button.setAttribute('data-bs-toggle', 'collapse'); - button.setAttribute('data-bs-target', `#${collapseId}`); - button.setAttribute('aria-expanded', 'true'); - button.setAttribute('aria-controls', collapseId); - button.style.padding = '10px'; - - if (payload.event === 'meetingStarted') { - button.classList.add('bg-success'); - } - if (payload.event === 'meetingEnded') { - button.classList.add('bg-danger'); - } - if (payload.event.includes('recording')) { - button.classList.add('bg-warning'); - } - // Format the header text with event name and timestamp - const date = new Date(payload.creationDate); - - const formattedDate = date.toLocaleString('es-ES', { - // year: 'numeric', - // month: '2-digit', - // day: '2-digit', - hour: '2-digit', - minute: '2-digit', - second: '2-digit', - hour12: false, - }); - button.innerHTML = `[${formattedDate}] ${payload.event}`; - - // Create collapsible content container - const collapseDiv = document.createElement('div'); - collapseDiv.id = collapseId; - collapseDiv.className = 'accordion-collapse collapse'; - collapseDiv.setAttribute('aria-labelledby', headerId); - collapseDiv.setAttribute('data-bs-parent', '#webhook-log-list'); - - // Create body content - const bodyDiv = document.createElement('div'); - bodyDiv.className = 'accordion-body'; - - // Format JSON with syntax highlighting if possible - const formattedJson = JSON.stringify(payload, null, 2); - bodyDiv.innerHTML = `
${escapeHtml(
-				formattedJson
-			)}
`; - - // Assemble the components - header.appendChild(button); - collapseDiv.appendChild(bodyDiv); - accordionItem.appendChild(header); - accordionItem.appendChild(collapseDiv); - - // Insert at the top of the list (latest events first) - if (webhookLogList.firstChild) { - webhookLogList.insertBefore(accordionItem, webhookLogList.firstChild); - } else { - webhookLogList.appendChild(accordionItem); - } - - // Limit the number of items to prevent performance issues - const maxItems = 50; - while (webhookLogList.children.length > maxItems) { - webhookLogList.removeChild(webhookLogList.lastChild!); - } + socket.on('webhookEvent', (event: any) => { + console.log('Webhook received:', event); + const isMeetingEnded = event.event === 'meetingEnded'; + const roomId = event.data.roomId; + if (roomId) { + saveWebhookEventToStorage(roomId, event); } + + addWebhookEventElement(event); + + // Clean up the previous events + if (isMeetingEnded) clearWebhookEventsByRoom(roomId); }); }; +const addWebhookEventElement = (event: any) => { + const webhookLogList = document.getElementById('webhook-log-list'); + if (webhookLogList) { + // Create unique IDs for this accordion item + const itemId = event.creationDate; + const headerClassName = `webhook-${event.event}`; + const collapseId = `collapse-${itemId}`; + + // Create accordion item container + const accordionItem = document.createElement('div'); + accordionItem.className = 'accordion-item'; + + // Create header + const header = document.createElement('h2'); + header.classList.add(headerClassName, 'accordion-header'); + + // Create header button + const button = document.createElement('button'); + button.className = 'accordion-button'; + button.type = 'button'; + button.setAttribute('data-bs-toggle', 'collapse'); + button.setAttribute('data-bs-target', `#${collapseId}`); + button.setAttribute('aria-expanded', 'true'); + button.setAttribute('aria-controls', collapseId); + button.style.padding = '10px'; + + if (event.event === 'meetingStarted') { + button.classList.add('bg-success'); + } + if (event.event === 'meetingEnded') { + button.classList.add('bg-danger'); + } + if (event.event.includes('recording')) { + button.classList.add('bg-warning'); + } + // Format the header text with event name and timestamp + const date = new Date(event.creationDate); + + const formattedDate = date.toLocaleString('es-ES', { + // year: 'numeric', + // month: '2-digit', + // day: '2-digit', + hour: '2-digit', + minute: '2-digit', + second: '2-digit', + hour12: false, + }); + button.innerHTML = `[${formattedDate}] ${event.event}`; + + // Create collapsible content container + const collapseDiv = document.createElement('div'); + collapseDiv.id = collapseId; + collapseDiv.className = 'accordion-collapse collapse'; + collapseDiv.setAttribute('aria-labelledby', headerClassName); + collapseDiv.setAttribute('data-bs-parent', '#webhook-log-list'); + + // Create body content + const bodyDiv = document.createElement('div'); + bodyDiv.className = 'accordion-body'; + + // Format JSON with syntax highlighting if possible + const formattedJson = JSON.stringify(event, null, 2); + bodyDiv.innerHTML = `
${escapeHtml(
+			formattedJson
+		)}
`; + + // Assemble the components + header.appendChild(button); + collapseDiv.appendChild(bodyDiv); + accordionItem.appendChild(header); + accordionItem.appendChild(collapseDiv); + + // Insert at the top of the list (latest events first) + if (webhookLogList.firstChild) { + webhookLogList.insertBefore(accordionItem, webhookLogList.firstChild); + } else { + webhookLogList.appendChild(accordionItem); + } + + // Limit the number of items to prevent performance issues + const maxItems = 50; + while (webhookLogList.children.length > maxItems) { + webhookLogList.removeChild(webhookLogList.lastChild!); + } + } +}; + // Listen to events from openvidu-meet const listenWebComponentEvents = () => { @@ -156,6 +195,14 @@ const setUpWebComponentCommands = () => { }; document.addEventListener('DOMContentLoaded', () => { + const roomId = document.getElementById('room-id')?.textContent?.trim(); + if (!roomId) { + console.error('Room ID not found in the DOM'); + alert('Room ID not found in the DOM'); + return; + } + const events = getWebhookEventsFromStorage(roomId); + events.forEach((event) => addWebhookEventElement(event)); listenWebhookServerEvents(); listenWebComponentEvents(); setUpWebComponentCommands(); diff --git a/testapp/public/views/index.mustache b/testapp/public/views/index.mustache index 0d59fca..6b568b8 100644 --- a/testapp/public/views/index.mustache +++ b/testapp/public/views/index.mustache @@ -50,6 +50,11 @@ name="participantRole" value="moderator" /> + + {{/isModerator}} - + diff --git a/testapp/src/controllers/roomController.ts b/testapp/src/controllers/roomController.ts index ed9ae4a..aa8dc84 100644 --- a/testapp/src/controllers/roomController.ts +++ b/testapp/src/controllers/roomController.ts @@ -7,6 +7,7 @@ import { MeetWebhookEvent } from '../../../typings/src/webhook.model'; interface JoinRoomRequest { participantRole: ParticipantRole; roomUrl: string; + roomId: string; participantName?: string; } @@ -15,6 +16,7 @@ export const joinRoom = (req: Request, res: Response) => { const { participantRole, roomUrl, + roomId, participantName = 'User', } = req.body as JoinRoomRequest; if (!roomUrl) { @@ -24,6 +26,7 @@ export const joinRoom = (req: Request, res: Response) => { roomUrl, participantRole, participantName, + roomId, isModerator: participantRole === 'moderator', }); } catch (error) { diff --git a/testapp/src/index.ts b/testapp/src/index.ts index 0706ea3..935dacf 100644 --- a/testapp/src/index.ts +++ b/testapp/src/index.ts @@ -44,5 +44,5 @@ server.listen(PORT, () => { console.log(`Meet API URL: ${configService.meetApiUrl}`); console.log('-----------------------------------------'); console.log(''); - console.log('Visit http://localhost:3000/ to access the app'); + console.log(`Visit http://localhost:${PORT}/ to access the app`); });