From 1539613bf8b0211847896f54554819277bcc5c20 Mon Sep 17 00:00:00 2001 From: lebaudantoine Date: Tue, 26 Aug 2025 22:43:28 +0200 Subject: [PATCH] =?UTF-8?q?=E2=99=BB=EF=B8=8F(frontend)=20replace=20direct?= =?UTF-8?q?=20LiveKit=20calls=20with=20backend=20API=20endpoints?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Refactor frontend to use backend-mediated API calls instead of direct LiveKit client-side requests for participant management operations. Removes hacky direct LiveKit API usage in favor of proper server-side endpoints, improving security posture and following LiveKit's recommended architecture for participant control functionality. --- .../rooms/api/lowerHandParticipant.ts | 27 ++++++++++++++ .../api/lowerHandParticipants.ts | 2 +- .../{livekit => }/api/muteParticipant.ts | 32 ++++++---------- .../rooms/livekit/api/buildServerApiUrl.ts | 5 --- .../rooms/livekit/api/fetchServerApi.ts | 21 ----------- .../rooms/livekit/api/lowerHandParticipant.ts | 37 ------------------- .../components/ParticipantTileFocus.tsx | 2 +- .../Participants/HandRaisedListItem.tsx | 2 +- .../Participants/LowerAllHandsButton.tsx | 2 +- .../Participants/ParticipantListItem.tsx | 2 +- 10 files changed, 43 insertions(+), 89 deletions(-) create mode 100644 src/frontend/src/features/rooms/api/lowerHandParticipant.ts rename src/frontend/src/features/rooms/{livekit => }/api/lowerHandParticipants.ts (86%) rename src/frontend/src/features/rooms/{livekit => }/api/muteParticipant.ts (57%) delete mode 100644 src/frontend/src/features/rooms/livekit/api/buildServerApiUrl.ts delete mode 100644 src/frontend/src/features/rooms/livekit/api/fetchServerApi.ts delete mode 100644 src/frontend/src/features/rooms/livekit/api/lowerHandParticipant.ts diff --git a/src/frontend/src/features/rooms/api/lowerHandParticipant.ts b/src/frontend/src/features/rooms/api/lowerHandParticipant.ts new file mode 100644 index 00000000..05d959cb --- /dev/null +++ b/src/frontend/src/features/rooms/api/lowerHandParticipant.ts @@ -0,0 +1,27 @@ +import { Participant } from 'livekit-client' +import { fetchApi } from '@/api/fetchApi.ts' +import { useRoomData } from '@/features/rooms/livekit/hooks/useRoomData' + +export const useLowerHandParticipant = () => { + const data = useRoomData() + + const lowerHandParticipant = async (participant: Participant) => { + if (!data?.id) { + throw new Error('Room id is not available') + } + + const newAttributes = { + ...participant.attributes, + handRaisedAt: '', + } + + return await fetchApi(`rooms/${data.id}/update-participant/`, { + method: 'POST', + body: JSON.stringify({ + participant_identity: participant.identity, + attributes: newAttributes, + }), + }) + } + return { lowerHandParticipant } +} diff --git a/src/frontend/src/features/rooms/livekit/api/lowerHandParticipants.ts b/src/frontend/src/features/rooms/api/lowerHandParticipants.ts similarity index 86% rename from src/frontend/src/features/rooms/livekit/api/lowerHandParticipants.ts rename to src/frontend/src/features/rooms/api/lowerHandParticipants.ts index c61fed05..13de51f5 100644 --- a/src/frontend/src/features/rooms/livekit/api/lowerHandParticipants.ts +++ b/src/frontend/src/features/rooms/api/lowerHandParticipants.ts @@ -1,5 +1,5 @@ import { Participant } from 'livekit-client' -import { useLowerHandParticipant } from '@/features/rooms/livekit/api/lowerHandParticipant' +import { useLowerHandParticipant } from './lowerHandParticipant' export const useLowerHandParticipants = () => { const { lowerHandParticipant } = useLowerHandParticipant() diff --git a/src/frontend/src/features/rooms/livekit/api/muteParticipant.ts b/src/frontend/src/features/rooms/api/muteParticipant.ts similarity index 57% rename from src/frontend/src/features/rooms/livekit/api/muteParticipant.ts rename to src/frontend/src/features/rooms/api/muteParticipant.ts index 310f9a26..2bc81dd0 100644 --- a/src/frontend/src/features/rooms/livekit/api/muteParticipant.ts +++ b/src/frontend/src/features/rooms/api/muteParticipant.ts @@ -1,12 +1,11 @@ import { Participant, Track } from 'livekit-client' import Source = Track.Source -import { fetchServerApi } from './fetchServerApi' -import { buildServerApiUrl } from './buildServerApiUrl' -import { useRoomData } from '../hooks/useRoomData' +import { useRoomData } from '../livekit/hooks/useRoomData' import { useNotifyParticipants, NotificationType, } from '@/features/notifications' +import { fetchApi } from '@/api/fetchApi' export const useMuteParticipant = () => { const data = useRoomData() @@ -14,8 +13,8 @@ export const useMuteParticipant = () => { const { notifyParticipants } = useNotifyParticipants() const muteParticipant = async (participant: Participant) => { - if (!data || !data?.livekit) { - throw new Error('Room data is not available') + if (!data?.id) { + throw new Error('Room id is not available') } const trackSid = participant.getTrackPublication( Source.Microphone @@ -26,22 +25,13 @@ export const useMuteParticipant = () => { } try { - const response = await fetchServerApi( - buildServerApiUrl( - data.livekit.url, - 'twirp/livekit.RoomService/MutePublishedTrack' - ), - data.livekit.token, - { - method: 'POST', - body: JSON.stringify({ - room: data.livekit.room, - identity: participant.identity, - muted: true, - track_sid: trackSid, - }), - } - ) + const response = await fetchApi(`rooms/${data.id}/mute-participant/`, { + method: 'POST', + body: JSON.stringify({ + participant_identity: participant.identity, + track_sid: trackSid, + }), + }) await notifyParticipants({ type: NotificationType.ParticipantMuted, diff --git a/src/frontend/src/features/rooms/livekit/api/buildServerApiUrl.ts b/src/frontend/src/features/rooms/livekit/api/buildServerApiUrl.ts deleted file mode 100644 index f8d10f62..00000000 --- a/src/frontend/src/features/rooms/livekit/api/buildServerApiUrl.ts +++ /dev/null @@ -1,5 +0,0 @@ -export const buildServerApiUrl = (origin: string, path: string) => { - const sanitizedOrigin = origin.replace(/\/$/, '') - const sanitizedPath = path.replace(/^\//, '') - return `${sanitizedOrigin}/${sanitizedPath}` -} diff --git a/src/frontend/src/features/rooms/livekit/api/fetchServerApi.ts b/src/frontend/src/features/rooms/livekit/api/fetchServerApi.ts deleted file mode 100644 index 3fb6ed17..00000000 --- a/src/frontend/src/features/rooms/livekit/api/fetchServerApi.ts +++ /dev/null @@ -1,21 +0,0 @@ -import { ApiError } from '@/api/ApiError' - -export const fetchServerApi = async >( - url: string, - token: string, - options?: RequestInit -): Promise => { - const response = await fetch(url, { - ...options, - headers: { - 'Content-Type': 'application/json', - Authorization: `Bearer ${token}`, - ...options?.headers, - }, - }) - const result = await response.json() - if (!response.ok) { - throw new ApiError(response.status, result) - } - return result -} diff --git a/src/frontend/src/features/rooms/livekit/api/lowerHandParticipant.ts b/src/frontend/src/features/rooms/livekit/api/lowerHandParticipant.ts deleted file mode 100644 index 13b9691d..00000000 --- a/src/frontend/src/features/rooms/livekit/api/lowerHandParticipant.ts +++ /dev/null @@ -1,37 +0,0 @@ -import { Participant } from 'livekit-client' -import { fetchServerApi } from './fetchServerApi' -import { buildServerApiUrl } from './buildServerApiUrl' -import { useRoomData } from '../hooks/useRoomData' - -export const useLowerHandParticipant = () => { - const data = useRoomData() - - const lowerHandParticipant = (participant: Participant) => { - if (!data || !data?.livekit) { - throw new Error('Room data is not available') - } - - const newAttributes = { - ...participant.attributes, - handRaisedAt: '', - } - - return fetchServerApi( - buildServerApiUrl( - data.livekit.url, - 'twirp/livekit.RoomService/UpdateParticipant' - ), - data.livekit.token, - { - method: 'POST', - body: JSON.stringify({ - room: data.livekit.room, - identity: participant.identity, - attributes: newAttributes, - permission: participant.permissions, - }), - } - ) - } - return { lowerHandParticipant } -} diff --git a/src/frontend/src/features/rooms/livekit/components/ParticipantTileFocus.tsx b/src/frontend/src/features/rooms/livekit/components/ParticipantTileFocus.tsx index f38f6df5..8a0cc16d 100644 --- a/src/frontend/src/features/rooms/livekit/components/ParticipantTileFocus.tsx +++ b/src/frontend/src/features/rooms/livekit/components/ParticipantTileFocus.tsx @@ -20,7 +20,7 @@ import { useSidePanel } from '../hooks/useSidePanel' import { useFullScreen } from '../hooks/useFullScreen' import { Participant, Track } from 'livekit-client' import { MuteAlertDialog } from './MuteAlertDialog' -import { useMuteParticipant } from '../api/muteParticipant' +import { useMuteParticipant } from '@/features/rooms/api/muteParticipant' const ZoomButton = ({ trackRef, diff --git a/src/frontend/src/features/rooms/livekit/components/controls/Participants/HandRaisedListItem.tsx b/src/frontend/src/features/rooms/livekit/components/controls/Participants/HandRaisedListItem.tsx index 6d651fb0..e29503b2 100644 --- a/src/frontend/src/features/rooms/livekit/components/controls/Participants/HandRaisedListItem.tsx +++ b/src/frontend/src/features/rooms/livekit/components/controls/Participants/HandRaisedListItem.tsx @@ -4,11 +4,11 @@ import { HStack } from '@/styled-system/jsx' import { Text } from '@/primitives/Text' import { useTranslation } from 'react-i18next' import { Avatar } from '@/components/Avatar' +import { useLowerHandParticipant } from '@/features/rooms/api/lowerHandParticipant' import { getParticipantColor } from '@/features/rooms/utils/getParticipantColor' import { Participant } from 'livekit-client' import { isLocal } from '@/utils/livekit' import { RiHand } from '@remixicon/react' -import { useLowerHandParticipant } from '@/features/rooms/livekit/api/lowerHandParticipant.ts' import { Button } from '@/primitives' type HandRaisedListItemProps = { diff --git a/src/frontend/src/features/rooms/livekit/components/controls/Participants/LowerAllHandsButton.tsx b/src/frontend/src/features/rooms/livekit/components/controls/Participants/LowerAllHandsButton.tsx index 3f177bf9..4f38ffb7 100644 --- a/src/frontend/src/features/rooms/livekit/components/controls/Participants/LowerAllHandsButton.tsx +++ b/src/frontend/src/features/rooms/livekit/components/controls/Participants/LowerAllHandsButton.tsx @@ -1,7 +1,7 @@ import { Button } from '@/primitives' -import { useLowerHandParticipants } from '@/features/rooms/livekit/api/lowerHandParticipants' import { useTranslation } from 'react-i18next' import { Participant } from 'livekit-client' +import { useLowerHandParticipants } from '@/features/rooms/api/lowerHandParticipants' type LowerAllHandsButtonProps = { participants: Array diff --git a/src/frontend/src/features/rooms/livekit/components/controls/Participants/ParticipantListItem.tsx b/src/frontend/src/features/rooms/livekit/components/controls/Participants/ParticipantListItem.tsx index 11d483ce..63232694 100644 --- a/src/frontend/src/features/rooms/livekit/components/controls/Participants/ParticipantListItem.tsx +++ b/src/frontend/src/features/rooms/livekit/components/controls/Participants/ParticipantListItem.tsx @@ -15,8 +15,8 @@ import Source = Track.Source import { RiMicFill, RiMicOffFill } from '@remixicon/react' import { Button } from '@/primitives' import { useState } from 'react' -import { useMuteParticipant } from '@/features/rooms/livekit/api/muteParticipant' import { MuteAlertDialog } from '../../MuteAlertDialog' +import { useMuteParticipant } from '@/features/rooms/api/muteParticipant' type MicIndicatorProps = { participant: Participant