feat(room): роль организатора в метаданных LiveKit-токена
Предварительная работа для очереди рук и принудительного мьюта (B1/B2): build_join кладёт is_organizer:true в метаданные токена организатора (создатель мгновенной конференции и владелец при обычном входе). Метаданные токена — только подсказка для UI (см. предупреждение в докстринге build_join), любое серверное действие организатора обязано перепроверяться по conference.owner_id в БД — так и сделано в mute_participant (B2). На фронте — общий парсер метаданных участника (lib/participantMetadata.ts, переиспользован в RoomParticipantTile вместо локальной копии) и хук useIsOrganizer (читает подсказку для локального участника через useLocalParticipant — вызывается только внутри LiveKitRoom).
This commit is contained in:
13
frontend/src/hooks/useIsOrganizer.ts
Normal file
13
frontend/src/hooks/useIsOrganizer.ts
Normal file
@@ -0,0 +1,13 @@
|
||||
import { useLocalParticipant } from '@livekit/components-react'
|
||||
import { parseParticipantMetadata } from '@/lib/participantMetadata'
|
||||
|
||||
/**
|
||||
* Организатор ли ТЕКУЩИЙ (локальный) участник комнаты — читает подсказку
|
||||
* `is_organizer` из метаданных собственного LiveKit-токена (см.
|
||||
* `lib/participantMetadata.ts`). Только для UI (показать/скрыть кнопки
|
||||
* организатора) — серверные действия перепроверяют права по БД сами.
|
||||
*/
|
||||
export function useIsOrganizer(): boolean {
|
||||
const { localParticipant } = useLocalParticipant()
|
||||
return Boolean(parseParticipantMetadata(localParticipant.metadata).is_organizer)
|
||||
}
|
||||
26
frontend/src/lib/participantMetadata.ts
Normal file
26
frontend/src/lib/participantMetadata.ts
Normal file
@@ -0,0 +1,26 @@
|
||||
/**
|
||||
* Метаданные участника из LiveKit access-токена (см. `AccessToken.with_metadata`
|
||||
* на backend, `services/conference_access.py::build_join`) — JSON
|
||||
* `{"avatar_url"?: string, "is_organizer"?: true}`. У гостей и участников без
|
||||
* аватара/прав организатора соответствующие поля отсутствуют.
|
||||
*/
|
||||
export interface ParticipantMetadata {
|
||||
avatar_url?: string | null
|
||||
/**
|
||||
* Подсказка для UI — организатор ли участник. НЕ источник авторизации:
|
||||
* метаданные читает и потенциально может подделать сам клиент. Любое
|
||||
* серверное действие организатора (например, принудительный мьют)
|
||||
* перепроверяется backend'ом по владельцу конференции в БД.
|
||||
*/
|
||||
is_organizer?: boolean
|
||||
}
|
||||
|
||||
/** Разобрать `participant.metadata` — пустой объект, если поля нет, метаданные пусты или невалидны. */
|
||||
export function parseParticipantMetadata(metadata: string | undefined): ParticipantMetadata {
|
||||
if (!metadata) return {}
|
||||
try {
|
||||
return JSON.parse(metadata) as ParticipantMetadata
|
||||
} catch {
|
||||
return {}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user