refactor: rename anonymous room access to access across all codebase
- Updated the MeetRoom interface to replace anonymous access configuration with a unified access configuration. - Refactored RoomService to handle access configuration for both anonymous and registered users. - Modified tests to reflect changes in access configuration structure. - Updated frontend components to use the new access configuration for meeting URLs and permissions. - Ensured backward compatibility by adjusting API endpoints and request/response types.
This commit is contained in:
parent
c563860758
commit
63d72c994b
@ -9,5 +9,5 @@ schema:
|
||||
type: string
|
||||
examples:
|
||||
basic:
|
||||
value: 'roomId,roomName,accessUrl'
|
||||
value: 'roomId,roomName,status'
|
||||
summary: Only return basic room information
|
||||
|
||||
@ -0,0 +1,9 @@
|
||||
description: Room access configuration update options
|
||||
required: true
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: object
|
||||
properties:
|
||||
access:
|
||||
$ref: '../schemas/meet-room-access-config.yaml'
|
||||
@ -1,9 +0,0 @@
|
||||
description: Room anonymous access configuration update options
|
||||
required: true
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: object
|
||||
properties:
|
||||
config:
|
||||
$ref: '../schemas/meet-room-anonymous-config.yaml'
|
||||
@ -70,14 +70,20 @@ content:
|
||||
roomName: room
|
||||
owner: 'admin'
|
||||
creationDate: 1620000000000
|
||||
anonymous:
|
||||
moderator:
|
||||
enabled: true
|
||||
accessUrl: 'https://example.com/room/room-123?secret=123456'
|
||||
speaker:
|
||||
enabled: true
|
||||
accessUrl: 'https://example.com/room/room-123?secret=654321'
|
||||
accessUrl: 'https://example.com/room/room-123'
|
||||
access:
|
||||
anonymous:
|
||||
moderator:
|
||||
enabled: true
|
||||
url: 'https://example.com/room/room-123?secret=123456'
|
||||
speaker:
|
||||
enabled: true
|
||||
url: 'https://example.com/room/room-123?secret=654321'
|
||||
recording:
|
||||
enabled: true
|
||||
url: 'https://example.com/room/room-123/recordings?secret=987654'
|
||||
registered:
|
||||
enabled: false
|
||||
url: 'https://example.com/room/room-123'
|
||||
status: closed
|
||||
meetingEndAction: none
|
||||
_extraFields:
|
||||
@ -94,14 +100,20 @@ content:
|
||||
roomName: room
|
||||
owner: 'admin'
|
||||
creationDate: 1620000000000
|
||||
anonymous:
|
||||
moderator:
|
||||
enabled: true
|
||||
accessUrl: 'https://example.com/room/room-123?secret=123456'
|
||||
speaker:
|
||||
enabled: true
|
||||
accessUrl: 'https://example.com/room/room-123?secret=654321'
|
||||
accessUrl: 'https://example.com/room/room-123'
|
||||
access:
|
||||
anonymous:
|
||||
moderator:
|
||||
enabled: true
|
||||
url: 'https://example.com/room/room-123?secret=123456'
|
||||
speaker:
|
||||
enabled: true
|
||||
url: 'https://example.com/room/room-123?secret=654321'
|
||||
recording:
|
||||
enabled: true
|
||||
url: 'https://example.com/room/room-123/recordings?secret=987654'
|
||||
registered:
|
||||
enabled: false
|
||||
url: 'https://example.com/room/room-123'
|
||||
status: active_meeting
|
||||
meetingEndAction: delete
|
||||
_extraFields:
|
||||
@ -118,14 +130,20 @@ content:
|
||||
roomName: room
|
||||
owner: 'admin'
|
||||
creationDate: 1620000000000
|
||||
anonymous:
|
||||
moderator:
|
||||
enabled: true
|
||||
accessUrl: 'https://example.com/room/room-123?secret=123456'
|
||||
speaker:
|
||||
enabled: true
|
||||
accessUrl: 'https://example.com/room/room-123?secret=654321'
|
||||
accessUrl: 'https://example.com/room/room-123'
|
||||
access:
|
||||
anonymous:
|
||||
moderator:
|
||||
enabled: true
|
||||
url: 'https://example.com/room/room-123?secret=123456'
|
||||
speaker:
|
||||
enabled: true
|
||||
url: 'https://example.com/room/room-123?secret=654321'
|
||||
recording:
|
||||
enabled: true
|
||||
url: 'https://example.com/room/room-123/recordings?secret=987654'
|
||||
registered:
|
||||
enabled: false
|
||||
url: 'https://example.com/room/room-123'
|
||||
status: active_meeting
|
||||
meetingEndAction: close
|
||||
_extraFields:
|
||||
@ -142,14 +160,20 @@ content:
|
||||
roomName: room
|
||||
owner: 'admin'
|
||||
creationDate: 1620000000000
|
||||
anonymous:
|
||||
moderator:
|
||||
enabled: true
|
||||
accessUrl: 'https://example.com/room/room-123?secret=123456'
|
||||
speaker:
|
||||
enabled: true
|
||||
accessUrl: 'https://example.com/room/room-123?secret=654321'
|
||||
accessUrl: 'https://example.com/room/room-123'
|
||||
access:
|
||||
anonymous:
|
||||
moderator:
|
||||
enabled: true
|
||||
url: 'https://example.com/room/room-123?secret=123456'
|
||||
speaker:
|
||||
enabled: true
|
||||
url: 'https://example.com/room/room-123?secret=654321'
|
||||
recording:
|
||||
enabled: true
|
||||
url: 'https://example.com/room/room-123/recordings?secret=987654'
|
||||
registered:
|
||||
enabled: false
|
||||
url: 'https://example.com/room/room-123'
|
||||
status: active_meeting
|
||||
meetingEndAction: delete
|
||||
_extraFields:
|
||||
@ -166,14 +190,20 @@ content:
|
||||
roomName: room
|
||||
owner: 'admin'
|
||||
creationDate: 1620000000000
|
||||
anonymous:
|
||||
moderator:
|
||||
enabled: true
|
||||
accessUrl: 'https://example.com/room/room-123?secret=123456'
|
||||
speaker:
|
||||
enabled: true
|
||||
accessUrl: 'https://example.com/room/room-123?secret=654321'
|
||||
accessUrl: 'https://example.com/room/room-123'
|
||||
access:
|
||||
anonymous:
|
||||
moderator:
|
||||
enabled: true
|
||||
url: 'https://example.com/room/room-123?secret=123456'
|
||||
speaker:
|
||||
enabled: true
|
||||
url: 'https://example.com/room/room-123?secret=654321'
|
||||
recording:
|
||||
enabled: true
|
||||
url: 'https://example.com/room/room-123/recordings?secret=987654'
|
||||
registered:
|
||||
enabled: false
|
||||
url: 'https://example.com/room/room-123'
|
||||
status: active_meeting
|
||||
meetingEndAction: close
|
||||
_extraFields:
|
||||
|
||||
@ -9,7 +9,7 @@ content:
|
||||
_extraFields:
|
||||
type: array
|
||||
description: >
|
||||
List of extra fields that can be included in the response based on the `X-ExtraFields` header.
|
||||
List of extra fields that can be included in the response based on the `X-ExtraFields` header.
|
||||
items:
|
||||
type: string
|
||||
example: config
|
||||
@ -58,17 +58,23 @@ content:
|
||||
canReadChat: true
|
||||
canWriteChat: true
|
||||
canChangeVirtualBackground: true
|
||||
anonymous:
|
||||
moderator:
|
||||
enabled: true
|
||||
accessUrl: 'https://example.com/room/room-123?secret=123456'
|
||||
speaker:
|
||||
enabled: true
|
||||
accessUrl: 'https://example.com/room/room-123?secret=654321'
|
||||
accessUrl: 'https://example.com/room/room-123'
|
||||
access:
|
||||
anonymous:
|
||||
moderator:
|
||||
enabled: true
|
||||
url: 'https://example.com/room/room-123?secret=123456'
|
||||
speaker:
|
||||
enabled: true
|
||||
url: 'https://example.com/room/room-123?secret=654321'
|
||||
recording:
|
||||
enabled: true
|
||||
url: 'https://example.com/room/room-123/recordings?secret=987654'
|
||||
registered:
|
||||
enabled: false
|
||||
url: 'https://example.com/room/room-123'
|
||||
status: open
|
||||
meetingEndAction: none
|
||||
_extraFields: ["config"]
|
||||
_extraFields: ['config']
|
||||
headers:
|
||||
Location:
|
||||
description: URL of the newly created room
|
||||
|
||||
@ -61,14 +61,20 @@ content:
|
||||
roomName: room
|
||||
owner: 'admin'
|
||||
creationDate: 1620000000000
|
||||
anonymous:
|
||||
moderator:
|
||||
enabled: true
|
||||
accessUrl: 'https://example.com/room/room-123?secret=123456'
|
||||
speaker:
|
||||
enabled: true
|
||||
accessUrl: 'https://example.com/room/room-123?secret=654321'
|
||||
accessUrl: 'https://example.com/room/room-123'
|
||||
access:
|
||||
anonymous:
|
||||
moderator:
|
||||
enabled: true
|
||||
url: 'https://example.com/room/room-123?secret=123456'
|
||||
speaker:
|
||||
enabled: true
|
||||
url: 'https://example.com/room/room-123?secret=654321'
|
||||
recording:
|
||||
enabled: true
|
||||
url: 'https://example.com/room/room-123/recordings?secret=987654'
|
||||
registered:
|
||||
enabled: false
|
||||
url: 'https://example.com/room/room-123'
|
||||
status: closed
|
||||
meetingEndAction: none
|
||||
_extraFields:
|
||||
@ -86,14 +92,20 @@ content:
|
||||
roomName: room
|
||||
owner: 'admin'
|
||||
creationDate: 1620000000000
|
||||
anonymous:
|
||||
moderator:
|
||||
enabled: true
|
||||
accessUrl: 'https://example.com/room/room-123?secret=123456'
|
||||
speaker:
|
||||
enabled: true
|
||||
accessUrl: 'https://example.com/room/room-123?secret=654321'
|
||||
accessUrl: 'https://example.com/room/room-123'
|
||||
access:
|
||||
anonymous:
|
||||
moderator:
|
||||
enabled: true
|
||||
url: 'https://example.com/room/room-123?secret=123456'
|
||||
speaker:
|
||||
enabled: true
|
||||
url: 'https://example.com/room/room-123?secret=654321'
|
||||
recording:
|
||||
enabled: true
|
||||
url: 'https://example.com/room/room-123/recordings?secret=987654'
|
||||
registered:
|
||||
enabled: false
|
||||
url: 'https://example.com/room/room-123'
|
||||
status: active_meeting
|
||||
meetingEndAction: close
|
||||
_extraFields:
|
||||
|
||||
@ -36,14 +36,20 @@ content:
|
||||
roomName: room
|
||||
owner: 'admin'
|
||||
creationDate: 1620000000000
|
||||
anonymous:
|
||||
moderator:
|
||||
enabled: true
|
||||
accessUrl: 'https://example.com/room/room-123?secret=123456'
|
||||
speaker:
|
||||
enabled: true
|
||||
accessUrl: 'https://example.com/room/room-123?secret=654321'
|
||||
accessUrl: 'https://example.com/room/room-123'
|
||||
access:
|
||||
anonymous:
|
||||
moderator:
|
||||
enabled: true
|
||||
url: 'https://example.com/room/room-123?secret=123456'
|
||||
speaker:
|
||||
enabled: true
|
||||
url: 'https://example.com/room/room-123?secret=654321'
|
||||
recording:
|
||||
enabled: true
|
||||
url: 'https://example.com/room/room-123/recordings?secret=987654'
|
||||
registered:
|
||||
enabled: false
|
||||
url: 'https://example.com/room/room-123'
|
||||
status: active_meeting
|
||||
meetingEndAction: delete
|
||||
_extraFields:
|
||||
@ -57,14 +63,20 @@ content:
|
||||
roomName: room
|
||||
owner: 'admin'
|
||||
creationDate: 1620000000000
|
||||
anonymous:
|
||||
moderator:
|
||||
enabled: true
|
||||
accessUrl: 'https://example.com/room/room-123?secret=123456'
|
||||
speaker:
|
||||
enabled: true
|
||||
accessUrl: 'https://example.com/room/room-123?secret=654321'
|
||||
accessUrl: 'https://example.com/room/room-123'
|
||||
access:
|
||||
anonymous:
|
||||
moderator:
|
||||
enabled: true
|
||||
url: 'https://example.com/room/room-123?secret=123456'
|
||||
speaker:
|
||||
enabled: true
|
||||
url: 'https://example.com/room/room-123?secret=654321'
|
||||
recording:
|
||||
enabled: true
|
||||
url: 'https://example.com/room/room-123/recordings?secret=987654'
|
||||
registered:
|
||||
enabled: false
|
||||
url: 'https://example.com/room/room-123'
|
||||
status: active_meeting
|
||||
meetingEndAction: delete
|
||||
_extraFields:
|
||||
@ -78,14 +90,20 @@ content:
|
||||
roomName: room
|
||||
owner: 'admin'
|
||||
creationDate: 1620000000000
|
||||
anonymous:
|
||||
moderator:
|
||||
enabled: true
|
||||
accessUrl: 'https://example.com/room/room-123?secret=123456'
|
||||
speaker:
|
||||
enabled: true
|
||||
accessUrl: 'https://example.com/room/room-123?secret=654321'
|
||||
accessUrl: 'https://example.com/room/room-123'
|
||||
access:
|
||||
anonymous:
|
||||
moderator:
|
||||
enabled: true
|
||||
url: 'https://example.com/room/room-123?secret=123456'
|
||||
speaker:
|
||||
enabled: true
|
||||
url: 'https://example.com/room/room-123?secret=654321'
|
||||
recording:
|
||||
enabled: true
|
||||
url: 'https://example.com/room/room-123/recordings?secret=987654'
|
||||
registered:
|
||||
enabled: false
|
||||
url: 'https://example.com/room/room-123'
|
||||
status: active_meeting
|
||||
meetingEndAction: close
|
||||
_extraFields:
|
||||
|
||||
@ -15,7 +15,7 @@ content:
|
||||
example: config
|
||||
examples:
|
||||
default_room_details:
|
||||
summary: Full room details response
|
||||
summary: Full room details response without extra fields
|
||||
value:
|
||||
roomId: 'room-123'
|
||||
roomName: 'room'
|
||||
@ -58,14 +58,20 @@ content:
|
||||
canReadChat: true
|
||||
canWriteChat: true
|
||||
canChangeVirtualBackground: true
|
||||
anonymous:
|
||||
moderator:
|
||||
enabled: true
|
||||
accessUrl: 'https://example.com/room/room-123?secret=123456'
|
||||
speaker:
|
||||
enabled: true
|
||||
accessUrl: 'https://example.com/room/room-123?secret=654321'
|
||||
accessUrl: 'https://example.com/room/room-123'
|
||||
access:
|
||||
anonymous:
|
||||
moderator:
|
||||
enabled: true
|
||||
url: 'https://example.com/room/room-123?secret=123456'
|
||||
speaker:
|
||||
enabled: true
|
||||
url: 'https://example.com/room/room-123?secret=654321'
|
||||
recording:
|
||||
enabled: true
|
||||
url: 'https://example.com/room/room-123/recordings?secret=987654'
|
||||
registered:
|
||||
enabled: false
|
||||
url: 'https://example.com/room/room-123'
|
||||
status: open
|
||||
meetingEndAction: none
|
||||
_extraFields:
|
||||
@ -73,16 +79,6 @@ content:
|
||||
|
||||
fields=roomId,roomName,creationDate,autoDeletionDate,config:
|
||||
summary: Room details with roomId, roomName, creationDate, autoDeletionDate, and config
|
||||
value:
|
||||
roomId: 'room-123'
|
||||
roomName: 'room'
|
||||
creationDate: 1620000000000
|
||||
autoDeletionDate: 1900000000000
|
||||
_extraFields:
|
||||
- config
|
||||
|
||||
extraFields=config:
|
||||
summary: Room details with expanded config
|
||||
value:
|
||||
roomId: 'room-123'
|
||||
roomName: 'room'
|
||||
@ -103,15 +99,98 @@ content:
|
||||
enabled: true
|
||||
_extraFields:
|
||||
- config
|
||||
fields=anonymous:
|
||||
summary: Response containing only anonymous access configuration
|
||||
|
||||
extraFields=config:
|
||||
summary: Room details with expanded config
|
||||
value:
|
||||
anonymous:
|
||||
roomId: 'room-123'
|
||||
roomName: 'room'
|
||||
owner: 'admin'
|
||||
creationDate: 1620000000000
|
||||
autoDeletionDate: 1900000000000
|
||||
autoDeletionPolicy:
|
||||
withMeeting: when_meeting_ends
|
||||
withRecordings: close
|
||||
config:
|
||||
chat:
|
||||
enabled: true
|
||||
recording:
|
||||
enabled: true
|
||||
layout: grid
|
||||
encoding: H264_720P_30
|
||||
virtualBackground:
|
||||
enabled: true
|
||||
e2ee:
|
||||
enabled: false
|
||||
captions:
|
||||
enabled: true
|
||||
roles:
|
||||
moderator:
|
||||
enabled: true
|
||||
accessUrl: 'https://example.com/room/room-123?secret=123456'
|
||||
permissions:
|
||||
canRecord: true
|
||||
canRetrieveRecordings: true
|
||||
canDeleteRecordings: true
|
||||
canJoinMeeting: true
|
||||
canShareAccessLinks: true
|
||||
canMakeModerator: true
|
||||
canKickParticipants: true
|
||||
canEndMeeting: true
|
||||
canPublishVideo: true
|
||||
canPublishAudio: true
|
||||
canShareScreen: true
|
||||
canReadChat: true
|
||||
canWriteChat: true
|
||||
canChangeVirtualBackground: true
|
||||
speaker:
|
||||
enabled: true
|
||||
accessUrl: 'https://example.com/room/room-123?secret=654321'
|
||||
permissions:
|
||||
canRecord: false
|
||||
canRetrieveRecordings: true
|
||||
canDeleteRecordings: false
|
||||
canJoinMeeting: true
|
||||
canShareAccessLinks: false
|
||||
canMakeModerator: false
|
||||
canKickParticipants: false
|
||||
canEndMeeting: false
|
||||
canPublishVideo: true
|
||||
canPublishAudio: true
|
||||
canShareScreen: true
|
||||
canReadChat: true
|
||||
canWriteChat: true
|
||||
canChangeVirtualBackground: true
|
||||
access:
|
||||
anonymous:
|
||||
moderator:
|
||||
enabled: true
|
||||
url: 'https://example.com/room/room-123?secret=123456'
|
||||
speaker:
|
||||
enabled: true
|
||||
url: 'https://example.com/room/room-123?secret=654321'
|
||||
recording:
|
||||
enabled: true
|
||||
url: 'https://example.com/room/room-123/recordings?secret=987654'
|
||||
registered:
|
||||
enabled: false
|
||||
url: 'https://example.com/room/room-123'
|
||||
status: open
|
||||
meetingEndAction: none
|
||||
_extraFields:
|
||||
- config
|
||||
fields=access:
|
||||
summary: Response containing only access configuration
|
||||
value:
|
||||
access:
|
||||
anonymous:
|
||||
moderator:
|
||||
enabled: true
|
||||
url: 'https://example.com/room/room-123?secret=123456'
|
||||
speaker:
|
||||
enabled: true
|
||||
url: 'https://example.com/room/room-123?secret=654321'
|
||||
recording:
|
||||
enabled: true
|
||||
url: 'https://example.com/room/room-123/recordings?secret=987654'
|
||||
registered:
|
||||
enabled: false
|
||||
url: 'https://example.com/room/room-123'
|
||||
_extraFields:
|
||||
- config
|
||||
|
||||
@ -20,7 +20,7 @@ content:
|
||||
|
||||
examples:
|
||||
default_room_details:
|
||||
summary: Full room details response with multiple rooms
|
||||
summary: Full room details without extra fields for multiple rooms
|
||||
value:
|
||||
rooms:
|
||||
- roomId: 'room-123'
|
||||
@ -64,14 +64,20 @@ content:
|
||||
canReadChat: true
|
||||
canWriteChat: true
|
||||
canChangeVirtualBackground: true
|
||||
anonymous:
|
||||
moderator:
|
||||
enabled: true
|
||||
accessUrl: 'https://example.com/room/room-123?secret=123456'
|
||||
speaker:
|
||||
enabled: true
|
||||
accessUrl: 'https://example.com/room/room-123?secret=654321'
|
||||
accessUrl: 'https://example.com/room/room-123'
|
||||
access:
|
||||
anonymous:
|
||||
moderator:
|
||||
enabled: true
|
||||
url: 'https://example.com/room/room-123?secret=123456'
|
||||
speaker:
|
||||
enabled: true
|
||||
url: 'https://example.com/room/room-123?secret=654321'
|
||||
recording:
|
||||
enabled: true
|
||||
url: 'https://example.com/room/room-123/recordings?secret=987654'
|
||||
registered:
|
||||
enabled: false
|
||||
url: 'https://example.com/room/room-123'
|
||||
status: open
|
||||
meetingEndAction: none
|
||||
- roomId: 'room-456'
|
||||
@ -115,14 +121,20 @@ content:
|
||||
canReadChat: true
|
||||
canWriteChat: true
|
||||
canChangeVirtualBackground: true
|
||||
anonymous:
|
||||
moderator:
|
||||
enabled: false
|
||||
accessUrl: 'https://example.com/room/room-456?secret=789012'
|
||||
speaker:
|
||||
access:
|
||||
anonymous:
|
||||
moderator:
|
||||
enabled: false
|
||||
url: 'https://example.com/room/room-456?secret=789012'
|
||||
speaker:
|
||||
enabled: true
|
||||
url: 'https://example.com/room/room-456?secret=210987'
|
||||
recording:
|
||||
enabled: true
|
||||
url: 'https://example.com/room/room-456/recordings?secret=345678'
|
||||
registered:
|
||||
enabled: true
|
||||
accessUrl: 'https://example.com/room/room-456?secret=210987'
|
||||
accessUrl: 'https://example.com/room/room-456'
|
||||
url: 'https://example.com/room/room-456'
|
||||
status: open
|
||||
meetingEndAction: none
|
||||
_extraFields:
|
||||
@ -199,14 +211,20 @@ content:
|
||||
canReadChat: true
|
||||
canWriteChat: true
|
||||
canChangeVirtualBackground: true
|
||||
anonymous:
|
||||
moderator:
|
||||
enabled: true
|
||||
accessUrl: 'https://example.com/room/room-123?secret=123456'
|
||||
speaker:
|
||||
enabled: true
|
||||
accessUrl: 'https://example.com/room/room-123?secret=654321'
|
||||
accessUrl: 'https://example.com/room/room-123'
|
||||
access:
|
||||
anonymous:
|
||||
moderator:
|
||||
enabled: true
|
||||
url: 'https://example.com/room/room-123?secret=123456'
|
||||
speaker:
|
||||
enabled: true
|
||||
url: 'https://example.com/room/room-123?secret=654321'
|
||||
recording:
|
||||
enabled: true
|
||||
url: 'https://example.com/room/room-123/recordings?secret=987654'
|
||||
registered:
|
||||
enabled: false
|
||||
url: 'https://example.com/room/room-123'
|
||||
status: open
|
||||
meetingEndAction: none
|
||||
- roomId: 'room-456'
|
||||
@ -273,14 +291,20 @@ content:
|
||||
canReadChat: true
|
||||
canWriteChat: true
|
||||
canChangeVirtualBackground: true
|
||||
anonymous:
|
||||
moderator:
|
||||
enabled: false
|
||||
accessUrl: 'https://example.com/room/room-456?secret=789012'
|
||||
speaker:
|
||||
access:
|
||||
anonymous:
|
||||
moderator:
|
||||
enabled: false
|
||||
url: 'https://example.com/room/room-456?secret=789012'
|
||||
speaker:
|
||||
enabled: true
|
||||
url: 'https://example.com/room/room-456?secret=210987'
|
||||
recording:
|
||||
enabled: true
|
||||
url: 'https://example.com/room/room-456/recordings?secret=345678'
|
||||
registered:
|
||||
enabled: true
|
||||
accessUrl: 'https://example.com/room/room-456?secret=210987'
|
||||
accessUrl: 'https://example.com/room/room-456'
|
||||
url: 'https://example.com/room/room-456'
|
||||
status: open
|
||||
meetingEndAction: none
|
||||
_extraFields:
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
description: Success response for updating room anonymous access configuration
|
||||
description: Success response for updating room access configuration
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
@ -7,4 +7,4 @@ content:
|
||||
message:
|
||||
type: string
|
||||
example:
|
||||
message: Anonymous access config for room 'room-123' updated successfully
|
||||
message: Access config for room 'room-123' updated successfully
|
||||
@ -0,0 +1,46 @@
|
||||
type: object
|
||||
properties:
|
||||
anonymous:
|
||||
type: object
|
||||
properties:
|
||||
moderator:
|
||||
type: object
|
||||
properties:
|
||||
enabled:
|
||||
type: boolean
|
||||
default: true
|
||||
example: true
|
||||
description: |
|
||||
Enables or disables anonymous access for the moderator role.
|
||||
speaker:
|
||||
type: object
|
||||
properties:
|
||||
enabled:
|
||||
type: boolean
|
||||
default: true
|
||||
example: true
|
||||
description: |
|
||||
Enables or disables anonymous access for the speaker role.
|
||||
recording:
|
||||
type: object
|
||||
properties:
|
||||
enabled:
|
||||
type: boolean
|
||||
default: true
|
||||
example: true
|
||||
description: |
|
||||
Enables or disables anonymous access for recordings in the room. This also controls whether individual anonymous recording URLs can be generated.
|
||||
registered:
|
||||
type: object
|
||||
properties:
|
||||
enabled:
|
||||
type: boolean
|
||||
default: true
|
||||
example: true
|
||||
description: |
|
||||
Enables or disables access for registered users.
|
||||
**Note**: admins, owner and members of the room will always have access regardless of this setting.
|
||||
description: |
|
||||
Configuration for room access.
|
||||
|
||||
All fields are optional. If not specified, current configuration will be maintained.
|
||||
@ -1,24 +0,0 @@
|
||||
type: object
|
||||
properties:
|
||||
moderator:
|
||||
type: object
|
||||
properties:
|
||||
enabled:
|
||||
type: boolean
|
||||
default: true
|
||||
example: true
|
||||
description: |
|
||||
Enables or disables anonymous access for the moderator role.
|
||||
speaker:
|
||||
type: object
|
||||
properties:
|
||||
enabled:
|
||||
type: boolean
|
||||
default: true
|
||||
example: true
|
||||
description: |
|
||||
Enables or disables anonymous access for the speaker role.
|
||||
description: |
|
||||
Configuration for anonymous access.
|
||||
|
||||
Both moderator and speaker fields are optional. If not specified, current configuration will be maintained.
|
||||
@ -68,12 +68,15 @@ properties:
|
||||
- Speaker: Permissions to publish audio and video streams.
|
||||
|
||||
You can customize this by providing partial permissions for each role (only specify the permissions you want to override).
|
||||
anonymous:
|
||||
$ref: meet-room-anonymous-config.yaml
|
||||
access:
|
||||
$ref: meet-room-access-config.yaml
|
||||
description: |
|
||||
Configuration for anonymous access to the room.
|
||||
Configuration for room access.
|
||||
|
||||
By default (if not specified), anonymous access is enabled for both moderators and speakers.
|
||||
You can customize this behavior by disabling anonymous access for specific roles (moderator/speaker) with per-role `enabled: false`
|
||||
By default (if not specified), anonymous access is enabled for both moderator and speaker roles, and for recording access.
|
||||
However, registered access is disabled by default.
|
||||
You can customize this behavior by disabling access for specific scopes and roles
|
||||
(`registered`, `anonymous.moderator`, `anonymous.speaker`, `anonymous.recording`) using `enabled: false`.
|
||||
|
||||
Permissions for anonymous users are determined by the room's role permissions.
|
||||
Permissions for anonymous users are determined by the room's role permissions. For registered users (who are not admins or members of the room),
|
||||
permissions will be the same as the speaker role.
|
||||
|
||||
@ -62,7 +62,7 @@ properties:
|
||||
Policy for automatic deletion when the room has recordings. Options are:
|
||||
- force: The room and its recordings will be deleted.
|
||||
- close: The room will be closed instead of deleted, maintaining its recordings.
|
||||
config (excluded by default):
|
||||
config:
|
||||
description: |
|
||||
Room configuration (chat, recording, virtual background, e2ee, captions).
|
||||
<br/><br/>
|
||||
@ -70,7 +70,7 @@ properties:
|
||||
**Excluded from responses by default to reduce payload size.**
|
||||
To include it in the response:
|
||||
- **POST requests:** use the `X-ExtraFields: config` header
|
||||
- **Other requests:** use either the `extraFields=config` query parameter or the `X-ExtraFields: config` header $ref: meet-room-config.yaml#/MeetRoomConfig
|
||||
- **Other requests:** use either the `extraFields=config` query parameter or the `X-ExtraFields: config` header
|
||||
$ref: meet-room-config.yaml#/MeetRoomConfig
|
||||
# maxParticipants:
|
||||
# type: integer
|
||||
@ -97,49 +97,71 @@ properties:
|
||||
$ref: meet-permissions.yaml
|
||||
description: >
|
||||
The complete set of permissions for the speaker role. These define what speakers can do in the meeting.
|
||||
anonymous:
|
||||
access:
|
||||
description: >
|
||||
Configuration for anonymous access to the room. Defines which roles have anonymous access enabled and their access URLs.
|
||||
Access configuration and generated access URLs for the room.
|
||||
type: object
|
||||
properties:
|
||||
moderator:
|
||||
anonymous:
|
||||
type: object
|
||||
properties:
|
||||
moderator:
|
||||
type: object
|
||||
properties:
|
||||
enabled:
|
||||
type: boolean
|
||||
example: true
|
||||
description: >
|
||||
Whether anonymous access for the moderator role is enabled.
|
||||
url:
|
||||
type: string
|
||||
format: uri
|
||||
example: 'http://localhost:6080/room/room-123?secret=123456'
|
||||
description: >
|
||||
The URL for anonymous moderators to access the room.
|
||||
speaker:
|
||||
type: object
|
||||
properties:
|
||||
enabled:
|
||||
type: boolean
|
||||
example: true
|
||||
description: >
|
||||
Whether anonymous access for the speaker role is enabled.
|
||||
url:
|
||||
type: string
|
||||
format: uri
|
||||
example: 'http://localhost:6080/room/room-123?secret=654321'
|
||||
description: >
|
||||
The URL for anonymous speakers to access the room.
|
||||
recording:
|
||||
type: object
|
||||
properties:
|
||||
enabled:
|
||||
type: boolean
|
||||
example: true
|
||||
description: >
|
||||
Whether anonymous access for recordings in the room is enabled.
|
||||
url:
|
||||
type: string
|
||||
format: uri
|
||||
example: 'http://localhost:6080/room/room-123?secret=987654'
|
||||
description: >
|
||||
The URL for anonymous access to the room's recordings.
|
||||
registered:
|
||||
type: object
|
||||
properties:
|
||||
enabled:
|
||||
type: boolean
|
||||
example: true
|
||||
description: >
|
||||
Whether anonymous access with moderator role is enabled.
|
||||
accessUrl:
|
||||
Whether access for registered users is enabled.
|
||||
**Note**: admins, owner and members of the room will always have access regardless of this setting.
|
||||
url:
|
||||
type: string
|
||||
format: uri
|
||||
example: 'http://localhost:6080/room/room-123?secret=123456'
|
||||
example: 'http://localhost:6080/room/room-123'
|
||||
description: >
|
||||
The URL for anonymous moderators to access the room.
|
||||
speaker:
|
||||
type: object
|
||||
properties:
|
||||
enabled:
|
||||
type: boolean
|
||||
example: true
|
||||
description: >
|
||||
Whether anonymous access with speaker role is enabled.
|
||||
accessUrl:
|
||||
type: string
|
||||
format: uri
|
||||
example: 'http://localhost:6080/room/room-123?secret=654321'
|
||||
description: >
|
||||
The URL for anonymous speakers to access the room.
|
||||
accessUrl:
|
||||
type: string
|
||||
format: uri
|
||||
example: 'http://localhost:6080/room/room-123'
|
||||
description: |
|
||||
The general access URL for authenticated users to join the room.
|
||||
|
||||
This URL should be used by:
|
||||
- The room owner (registered Meet user who created the room)
|
||||
- Registered Meet users who are members of the room
|
||||
The URL for registered users to access the room.
|
||||
status:
|
||||
type: string
|
||||
enum:
|
||||
|
||||
@ -17,8 +17,8 @@ paths:
|
||||
$ref: './paths/rooms.yaml#/~1rooms~1{roomId}~1config'
|
||||
/rooms/{roomId}/roles:
|
||||
$ref: './paths/rooms.yaml#/~1rooms~1{roomId}~1roles'
|
||||
/rooms/{roomId}/anonymous:
|
||||
$ref: './paths/rooms.yaml#/~1rooms~1{roomId}~1anonymous'
|
||||
/rooms/{roomId}/access:
|
||||
$ref: './paths/rooms.yaml#/~1rooms~1{roomId}~1access'
|
||||
/rooms/{roomId}/status:
|
||||
$ref: './paths/rooms.yaml#/~1rooms~1{roomId}~1status'
|
||||
/rooms/{roomId}/members:
|
||||
|
||||
@ -290,14 +290,15 @@
|
||||
$ref: '../components/responses/validation-error.yaml'
|
||||
'500':
|
||||
$ref: '../components/responses/internal-server-error.yaml'
|
||||
/rooms/{roomId}/anonymous:
|
||||
/rooms/{roomId}/access:
|
||||
put:
|
||||
operationId: updateRoomAnonymous
|
||||
summary: Update anonymous access config for a room
|
||||
operationId: updateRoomAccess
|
||||
summary: Update access config for a room
|
||||
description: |
|
||||
Updates the anonymous access configuration for the specified room.
|
||||
Updates the access configuration for the specified room.
|
||||
|
||||
This allows you to enable or disable anonymous access for specific roles (moderator/speaker).
|
||||
This allows you to enable or disable access for registered users and anonymous roles
|
||||
(moderator/speaker/recording).
|
||||
tags:
|
||||
- OpenVidu Meet - Rooms
|
||||
security:
|
||||
@ -306,10 +307,10 @@
|
||||
parameters:
|
||||
- $ref: '../components/parameters/room-id-path.yaml'
|
||||
requestBody:
|
||||
$ref: '../components/requestBodies/update-room-anonymous-request.yaml'
|
||||
$ref: '../components/requestBodies/update-room-access-request.yaml'
|
||||
responses:
|
||||
'200':
|
||||
$ref: '../components/responses/success-update-room-anonymous.yaml'
|
||||
$ref: '../components/responses/success-update-room-access.yaml'
|
||||
'401':
|
||||
$ref: '../components/responses/unauthorized-error.yaml'
|
||||
'403':
|
||||
|
||||
@ -257,18 +257,18 @@ export const updateRoomRoles = async (req: Request, res: Response) => {
|
||||
}
|
||||
};
|
||||
|
||||
export const updateRoomAnonymous = async (req: Request, res: Response) => {
|
||||
export const updateRoomAccess = async (req: Request, res: Response) => {
|
||||
const logger = container.get(LoggerService);
|
||||
const roomService = container.get(RoomService);
|
||||
const { anonymous } = req.body;
|
||||
const { access } = req.body;
|
||||
const { roomId } = req.params;
|
||||
|
||||
logger.verbose(`Updating anonymous access config for room '${roomId}'`);
|
||||
logger.verbose(`Updating access config for room '${roomId}'`);
|
||||
|
||||
try {
|
||||
await roomService.updateMeetRoomAnonymous(roomId, anonymous);
|
||||
return res.status(200).json({ message: `Anonymous access config for room '${roomId}' updated successfully` });
|
||||
await roomService.updateMeetRoomAccess(roomId, access);
|
||||
return res.status(200).json({ message: `Access config for room '${roomId}' updated successfully` });
|
||||
} catch (error) {
|
||||
handleError(res, error, `updating anonymous access config for room '${roomId}'`);
|
||||
handleError(res, error, `updating access config for room '${roomId}'`);
|
||||
}
|
||||
};
|
||||
|
||||
@ -2,7 +2,7 @@ import {
|
||||
MEET_ROOM_EXTRA_FIELDS,
|
||||
MEET_ROOM_FIELDS,
|
||||
MeetRoom,
|
||||
MeetRoomAnonymous,
|
||||
MeetRoomAccess,
|
||||
MeetRoomExtraField,
|
||||
MeetRoomField,
|
||||
MeetRoomMemberPermissions,
|
||||
@ -77,12 +77,20 @@ export class MeetRoomHelper {
|
||||
autoDeletionPolicy: room.autoDeletionPolicy,
|
||||
config: room.config,
|
||||
roles: room.roles,
|
||||
anonymous: {
|
||||
moderator: {
|
||||
enabled: room.anonymous.moderator.enabled
|
||||
access: {
|
||||
anonymous: {
|
||||
moderator: {
|
||||
enabled: room.access.anonymous.moderator.enabled
|
||||
},
|
||||
speaker: {
|
||||
enabled: room.access.anonymous.speaker.enabled
|
||||
},
|
||||
recording: {
|
||||
enabled: room.access.anonymous.recording.enabled
|
||||
}
|
||||
},
|
||||
speaker: {
|
||||
enabled: room.anonymous.speaker.enabled
|
||||
registered: {
|
||||
enabled: room.access.registered.enabled
|
||||
}
|
||||
}
|
||||
// maxParticipants: room.maxParticipants
|
||||
@ -90,25 +98,33 @@ export class MeetRoomHelper {
|
||||
}
|
||||
|
||||
/**
|
||||
* Extracts speaker and moderator secrets from MeetRoom anonymous access URLs.
|
||||
* Extracts speaker, moderator, and recording secrets from MeetRoom anonymous access URLs.
|
||||
*
|
||||
* This method parses the 'secret' query parameter from both speaker and moderator
|
||||
* This method parses the 'secret' query parameter from speaker, moderator, and recording
|
||||
* anonymous access URLs associated with the meeting room.
|
||||
*
|
||||
* @param room - The anonymous access configuration of the MeetRoom from which to extract secrets.
|
||||
* @param roomAccess - The access configuration of the MeetRoom from which to extract secrets.
|
||||
* @returns An object containing the extracted secrets with the following properties:
|
||||
* - speakerSecret: The secret extracted from the speaker anonymous access URL
|
||||
* - moderatorSecret: The secret extracted from the moderator anonymous access URL
|
||||
* - recordingSecret: The secret extracted from the recording anonymous access URL
|
||||
*/
|
||||
static extractSecretsFromRoom(anonymous: MeetRoomAnonymous): { speakerSecret: string; moderatorSecret: string } {
|
||||
const speakerUrl = anonymous.speaker.accessUrl;
|
||||
const moderatorUrl = anonymous.moderator.accessUrl;
|
||||
static extractSecretsFromRoom(roomAccess: MeetRoomAccess): {
|
||||
speakerSecret: string;
|
||||
moderatorSecret: string;
|
||||
recordingSecret: string;
|
||||
} {
|
||||
const speakerUrl = roomAccess.anonymous.speaker.url;
|
||||
const moderatorUrl = roomAccess.anonymous.moderator.url;
|
||||
const recordingUrl = roomAccess.anonymous.recording.url;
|
||||
|
||||
const parsedSpeakerUrl = new URL(speakerUrl);
|
||||
const speakerSecret = parsedSpeakerUrl.searchParams.get('secret') || '';
|
||||
const parsedModeratorUrl = new URL(moderatorUrl);
|
||||
const moderatorSecret = parsedModeratorUrl.searchParams.get('secret') || '';
|
||||
return { speakerSecret, moderatorSecret };
|
||||
const parsedRecordingUrl = new URL(recordingUrl);
|
||||
const recordingSecret = parsedRecordingUrl.searchParams.get('secret') || '';
|
||||
return { speakerSecret, moderatorSecret, recordingSecret };
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@ -8,7 +8,7 @@ import {
|
||||
RoomFiltersSchema,
|
||||
RoomOptionsSchema,
|
||||
RoomQueryFieldsSchema,
|
||||
UpdateRoomAnonymousReqSchema,
|
||||
UpdateRoomAccessReqSchema,
|
||||
UpdateRoomConfigReqSchema,
|
||||
UpdateRoomRolesReqSchema,
|
||||
UpdateRoomStatusReqSchema
|
||||
@ -152,8 +152,8 @@ export const validateUpdateRoomRolesReq = (req: Request, res: Response, next: Ne
|
||||
next();
|
||||
};
|
||||
|
||||
export const validateUpdateRoomAnonymousReq = (req: Request, res: Response, next: NextFunction) => {
|
||||
const { success, error, data } = UpdateRoomAnonymousReqSchema.safeParse(req.body);
|
||||
export const validateUpdateRoomAccessReq = (req: Request, res: Response, next: NextFunction) => {
|
||||
const { success, error, data } = UpdateRoomAccessReqSchema.safeParse(req.body);
|
||||
|
||||
if (!success) {
|
||||
return rejectUnprocessableRequest(res, error);
|
||||
|
||||
@ -1,4 +1,5 @@
|
||||
import { MeetRecordingEncodingPreset, MeetRecordingLayout } from '@openvidu-meet/typings';
|
||||
import { uid as secureUid } from 'uid/secure';
|
||||
import { MEET_ENV } from '../environment.js';
|
||||
import { generateSchemaMigrationName, SchemaMigrationMap, SchemaTransform } from '../models/migration.model.js';
|
||||
import { meetRoomCollectionName, MeetRoomDocument } from '../models/mongoose-schemas/room.schema.js';
|
||||
@ -63,17 +64,26 @@ const roomMigrationV2ToV3Transform: SchemaTransform<MeetRoomDocument> = (room) =
|
||||
}
|
||||
}
|
||||
};
|
||||
room.anonymous = {
|
||||
moderator: {
|
||||
enabled: true,
|
||||
accessUrl: legacyRoom.moderatorUrl!
|
||||
room.access = {
|
||||
anonymous: {
|
||||
moderator: {
|
||||
enabled: true,
|
||||
url: legacyRoom.moderatorUrl!
|
||||
},
|
||||
speaker: {
|
||||
enabled: true,
|
||||
url: legacyRoom.speakerUrl!
|
||||
},
|
||||
recording: {
|
||||
enabled: false,
|
||||
url: `/room/${room.roomId}/recordings?secret=${secureUid(10)}`
|
||||
}
|
||||
},
|
||||
speaker: {
|
||||
registered: {
|
||||
enabled: true,
|
||||
accessUrl: legacyRoom.speakerUrl!
|
||||
url: `/room/${room.roomId}`
|
||||
}
|
||||
};
|
||||
room.accessUrl = `/room/${room.roomId}`;
|
||||
room.rolesUpdatedAt = Date.now();
|
||||
|
||||
delete legacyRoom.moderatorUrl;
|
||||
|
||||
@ -200,26 +200,48 @@ const MeetRoomRolesSchema = new Schema(
|
||||
);
|
||||
|
||||
/**
|
||||
* Sub-schema for anonymous access configuration.
|
||||
* Sub-schema for access configuration.
|
||||
*/
|
||||
const MeetRoomAnonymousSchema = new Schema(
|
||||
const MeetRoomAccessSchema = new Schema(
|
||||
{
|
||||
moderator: {
|
||||
enabled: {
|
||||
type: Boolean,
|
||||
required: true
|
||||
anonymous: {
|
||||
moderator: {
|
||||
enabled: {
|
||||
type: Boolean,
|
||||
required: true
|
||||
},
|
||||
url: {
|
||||
type: String,
|
||||
required: true
|
||||
}
|
||||
},
|
||||
accessUrl: {
|
||||
type: String,
|
||||
required: true
|
||||
speaker: {
|
||||
enabled: {
|
||||
type: Boolean,
|
||||
required: true
|
||||
},
|
||||
url: {
|
||||
type: String,
|
||||
required: true
|
||||
}
|
||||
},
|
||||
recording: {
|
||||
enabled: {
|
||||
type: Boolean,
|
||||
required: true
|
||||
},
|
||||
url: {
|
||||
type: String,
|
||||
required: true
|
||||
}
|
||||
}
|
||||
},
|
||||
speaker: {
|
||||
registered: {
|
||||
enabled: {
|
||||
type: Boolean,
|
||||
required: true
|
||||
},
|
||||
accessUrl: {
|
||||
url: {
|
||||
type: String,
|
||||
required: true
|
||||
}
|
||||
@ -300,12 +322,8 @@ const MeetRoomSchema = new Schema<MeetRoomDocument>(
|
||||
type: MeetRoomRolesSchema,
|
||||
required: true
|
||||
},
|
||||
anonymous: {
|
||||
type: MeetRoomAnonymousSchema,
|
||||
required: true
|
||||
},
|
||||
accessUrl: {
|
||||
type: String,
|
||||
access: {
|
||||
type: MeetRoomAccessSchema,
|
||||
required: true
|
||||
},
|
||||
status: {
|
||||
|
||||
@ -11,7 +11,7 @@ import {
|
||||
MeetRecordingEncodingPreset,
|
||||
MeetRecordingLayout,
|
||||
MeetRecordingVideoCodec,
|
||||
MeetRoomAnonymousConfig,
|
||||
MeetRoomAccessConfig,
|
||||
MeetRoomAutoDeletionPolicy,
|
||||
MeetRoomCaptionsConfig,
|
||||
MeetRoomConfig,
|
||||
@ -296,13 +296,27 @@ const RoomRolesConfigSchema: z.ZodType<MeetRoomRolesConfig> = z.object({
|
||||
.optional()
|
||||
});
|
||||
|
||||
const RoomAnonymousConfigSchema: z.ZodType<MeetRoomAnonymousConfig> = z.object({
|
||||
moderator: z
|
||||
const RoomAccessConfigSchema: z.ZodType<MeetRoomAccessConfig> = z.object({
|
||||
anonymous: z
|
||||
.object({
|
||||
enabled: z.boolean()
|
||||
moderator: z
|
||||
.object({
|
||||
enabled: z.boolean()
|
||||
})
|
||||
.optional(),
|
||||
speaker: z
|
||||
.object({
|
||||
enabled: z.boolean()
|
||||
})
|
||||
.optional(),
|
||||
recording: z
|
||||
.object({
|
||||
enabled: z.boolean()
|
||||
})
|
||||
.optional()
|
||||
})
|
||||
.optional(),
|
||||
speaker: z
|
||||
registered: z
|
||||
.object({
|
||||
enabled: z.boolean()
|
||||
})
|
||||
@ -359,9 +373,13 @@ export const RoomOptionsSchema: z.ZodType<MeetRoomOptions> = z.object({
|
||||
captions: { enabled: true }
|
||||
}),
|
||||
roles: RoomRolesConfigSchema.optional(),
|
||||
anonymous: RoomAnonymousConfigSchema.optional().default({
|
||||
moderator: { enabled: true },
|
||||
speaker: { enabled: true }
|
||||
access: RoomAccessConfigSchema.optional().default({
|
||||
anonymous: {
|
||||
moderator: { enabled: true },
|
||||
speaker: { enabled: true },
|
||||
recording: { enabled: true }
|
||||
},
|
||||
registered: { enabled: false }
|
||||
})
|
||||
// maxParticipants: z
|
||||
// .number()
|
||||
@ -556,8 +574,8 @@ export const UpdateRoomRolesReqSchema = z.object({
|
||||
roles: RoomRolesConfigSchema
|
||||
});
|
||||
|
||||
export const UpdateRoomAnonymousReqSchema = z.object({
|
||||
anonymous: RoomAnonymousConfigSchema
|
||||
export const UpdateRoomAccessReqSchema = z.object({
|
||||
access: RoomAccessConfigSchema
|
||||
});
|
||||
|
||||
export const UpdateRoomStatusReqSchema = z.object({
|
||||
|
||||
@ -261,19 +261,25 @@ export class RoomRepository extends BaseRepository<MeetRoom, MeetRoomDocument> {
|
||||
* @returns Normalized partial room data
|
||||
*/
|
||||
private normalizeRoomForStorage(room: Partial<MeetRoom>): Partial<MeetRoom> {
|
||||
if (room.accessUrl) {
|
||||
room.accessUrl = this.extractPathFromUrl(room.accessUrl);
|
||||
const registeredUrl = room.access?.registered.url;
|
||||
const moderatorUrl = room.access?.anonymous.moderator.url;
|
||||
const speakerUrl = room.access?.anonymous.speaker.url;
|
||||
const recordingUrl = room.access?.anonymous.recording.url;
|
||||
|
||||
if (registeredUrl) {
|
||||
room.access!.registered.url = this.extractPathFromUrl(registeredUrl);
|
||||
}
|
||||
|
||||
const moderatorUrl = room.anonymous?.moderator.accessUrl;
|
||||
const speakerUrl = room.anonymous?.speaker.accessUrl;
|
||||
|
||||
if (moderatorUrl) {
|
||||
room.anonymous!.moderator.accessUrl = this.extractPathFromUrl(moderatorUrl);
|
||||
room.access!.anonymous.moderator.url = this.extractPathFromUrl(moderatorUrl);
|
||||
}
|
||||
|
||||
if (speakerUrl) {
|
||||
room.anonymous!.speaker.accessUrl = this.extractPathFromUrl(speakerUrl);
|
||||
room.access!.anonymous.speaker.url = this.extractPathFromUrl(speakerUrl);
|
||||
}
|
||||
|
||||
if (recordingUrl) {
|
||||
room.access!.anonymous.recording.url = this.extractPathFromUrl(recordingUrl);
|
||||
}
|
||||
|
||||
return room;
|
||||
@ -330,19 +336,25 @@ export class RoomRepository extends BaseRepository<MeetRoom, MeetRoomDocument> {
|
||||
private enrichRoomWithBaseUrls(room: MeetRoom): MeetRoom {
|
||||
const baseUrl = getBaseUrl();
|
||||
|
||||
if (room.accessUrl) {
|
||||
room.accessUrl = `${baseUrl}${room.accessUrl}`;
|
||||
const registeredUrl = room.access?.registered.url;
|
||||
const moderatorUrl = room.access?.anonymous.moderator.url;
|
||||
const speakerUrl = room.access?.anonymous.speaker.url;
|
||||
const recordingUrl = room.access?.anonymous.recording.url;
|
||||
|
||||
if (registeredUrl) {
|
||||
room.access!.registered.url = `${baseUrl}${registeredUrl}`;
|
||||
}
|
||||
|
||||
const moderatorUrl = room.anonymous?.moderator.accessUrl;
|
||||
const speakerUrl = room.anonymous?.speaker.accessUrl;
|
||||
|
||||
if (moderatorUrl) {
|
||||
room.anonymous!.moderator.accessUrl = `${baseUrl}${moderatorUrl}`;
|
||||
room.access!.anonymous.moderator.url = `${baseUrl}${moderatorUrl}`;
|
||||
}
|
||||
|
||||
if (speakerUrl) {
|
||||
room.anonymous!.speaker.accessUrl = `${baseUrl}${speakerUrl}`;
|
||||
room.access!.anonymous.speaker.url = `${baseUrl}${speakerUrl}`;
|
||||
}
|
||||
|
||||
if (recordingUrl) {
|
||||
room.access!.anonymous.recording.url = `${baseUrl}${recordingUrl}`;
|
||||
}
|
||||
|
||||
return room;
|
||||
|
||||
@ -22,7 +22,7 @@ import {
|
||||
validateDeleteRoomReq,
|
||||
validateGetRoomReq,
|
||||
validateGetRoomsReq,
|
||||
validateUpdateRoomAnonymousReq,
|
||||
validateUpdateRoomAccessReq,
|
||||
validateUpdateRoomConfigReq,
|
||||
validateUpdateRoomRolesReq,
|
||||
validateUpdateRoomStatusReq,
|
||||
@ -116,12 +116,12 @@ roomRouter.put(
|
||||
roomCtrl.updateRoomRoles
|
||||
);
|
||||
roomRouter.put(
|
||||
'/:roomId/anonymous',
|
||||
'/:roomId/access',
|
||||
withAuth(apiKeyValidator, accessTokenValidator(MeetUserRole.ADMIN, MeetUserRole.USER)),
|
||||
withValidRoomId,
|
||||
validateUpdateRoomAnonymousReq,
|
||||
validateUpdateRoomAccessReq,
|
||||
authorizeRoomManagement,
|
||||
roomCtrl.updateRoomAnonymous
|
||||
roomCtrl.updateRoomAccess
|
||||
);
|
||||
|
||||
// Room Member Routes
|
||||
|
||||
@ -418,10 +418,10 @@ export class RoomMemberService {
|
||||
} else {
|
||||
// If secret matches anonymous access URL secret, assign role and permissions based on it
|
||||
baseRole = await this.getRoomMemberRoleBySecret(roomId, secret);
|
||||
const { roles, anonymous } = await this.roomService.getMeetRoom(roomId, ['roles', 'anonymous']);
|
||||
const { roles, access } = await this.roomService.getMeetRoom(roomId, ['roles', 'access']);
|
||||
|
||||
// Check that anonymous access is enabled for the role
|
||||
if (!anonymous[baseRole].enabled) {
|
||||
if (!access.anonymous[baseRole].enabled) {
|
||||
throw errorAnonymousAccessDisabled(roomId, baseRole);
|
||||
}
|
||||
|
||||
@ -609,8 +609,8 @@ export class RoomMemberService {
|
||||
* @throws Error if the provided secret doesn't match any of the room's secrets (unauthorized)
|
||||
*/
|
||||
protected async getRoomMemberRoleBySecret(roomId: string, secret: string): Promise<MeetRoomMemberRole> {
|
||||
const { anonymous } = await this.roomService.getMeetRoom(roomId, ['anonymous']);
|
||||
const { moderatorSecret, speakerSecret } = MeetRoomHelper.extractSecretsFromRoom(anonymous);
|
||||
const { access } = await this.roomService.getMeetRoom(roomId, ['access']);
|
||||
const { moderatorSecret, speakerSecret } = MeetRoomHelper.extractSecretsFromRoom(access);
|
||||
|
||||
switch (secret) {
|
||||
case moderatorSecret:
|
||||
@ -778,7 +778,7 @@ export class RoomMemberService {
|
||||
newRole: MeetRoomMemberRole
|
||||
): Promise<void> {
|
||||
try {
|
||||
const { roles, anonymous } = await this.roomService.getMeetRoom(roomId, ['roles', 'anonymous']);
|
||||
const { roles, access } = await this.roomService.getMeetRoom(roomId, ['roles', 'access']);
|
||||
const participant = await this.getParticipantFromMeeting(roomId, participantIdentity);
|
||||
const metadata: MeetRoomMemberTokenMetadata = this.tokenService.parseRoomMemberTokenMetadata(
|
||||
participant.metadata
|
||||
@ -791,7 +791,7 @@ export class RoomMemberService {
|
||||
|
||||
await this.livekitService.updateParticipantMetadata(roomId, participantIdentity, JSON.stringify(metadata));
|
||||
|
||||
const { speakerSecret, moderatorSecret } = MeetRoomHelper.extractSecretsFromRoom(anonymous);
|
||||
const { speakerSecret, moderatorSecret } = MeetRoomHelper.extractSecretsFromRoom(access);
|
||||
const secret = newRole === MeetRoomMemberRole.MODERATOR ? moderatorSecret : speakerSecret;
|
||||
await this.frontendEventService.sendParticipantRoleUpdatedSignal(
|
||||
roomId,
|
||||
|
||||
@ -1,8 +1,8 @@
|
||||
import {
|
||||
MeetingEndAction,
|
||||
MeetRoom,
|
||||
MeetRoomAnonymous,
|
||||
MeetRoomAnonymousConfig,
|
||||
MeetRoomAccess,
|
||||
MeetRoomAccessConfig,
|
||||
MeetRoomConfig,
|
||||
MeetRoomDeletionErrorCode,
|
||||
MeetRoomDeletionPolicyWithMeeting,
|
||||
@ -81,7 +81,7 @@ export class RoomService {
|
||||
*
|
||||
*/
|
||||
async createMeetRoom(roomOptions: MeetRoomOptions): Promise<MeetRoom> {
|
||||
const { roomName, autoDeletionDate, autoDeletionPolicy, config, roles, anonymous } = roomOptions;
|
||||
const { roomName, autoDeletionDate, autoDeletionPolicy, config, roles, access } = roomOptions;
|
||||
|
||||
// Generate a unique room ID based on the room name
|
||||
const roomIdPrefix = MeetRoomHelper.createRoomIdPrefixFromRoomName(roomName!) || 'room';
|
||||
@ -135,14 +135,24 @@ export class RoomService {
|
||||
}
|
||||
};
|
||||
|
||||
const anonymousConfig: MeetRoomAnonymous = {
|
||||
moderator: {
|
||||
enabled: anonymous?.moderator?.enabled ?? true,
|
||||
accessUrl: `/room/${roomId}?secret=${secureUid(10)}`
|
||||
const accessConfig: MeetRoomAccess = {
|
||||
anonymous: {
|
||||
moderator: {
|
||||
enabled: access?.anonymous?.moderator?.enabled ?? true,
|
||||
url: `/room/${roomId}?secret=${secureUid(10)}`
|
||||
},
|
||||
speaker: {
|
||||
enabled: access?.anonymous?.speaker?.enabled ?? true,
|
||||
url: `/room/${roomId}?secret=${secureUid(10)}`
|
||||
},
|
||||
recording: {
|
||||
enabled: access?.anonymous?.recording?.enabled ?? true,
|
||||
url: `/room/${roomId}/recordings?secret=${secureUid(10)}`
|
||||
}
|
||||
},
|
||||
speaker: {
|
||||
enabled: anonymous?.speaker?.enabled ?? true,
|
||||
accessUrl: `/room/${roomId}?secret=${secureUid(10)}`
|
||||
registered: {
|
||||
enabled: access?.registered?.enabled ?? false,
|
||||
url: `/room/${roomId}`
|
||||
}
|
||||
};
|
||||
|
||||
@ -157,8 +167,7 @@ export class RoomService {
|
||||
autoDeletionPolicy: autoDeletionDate ? autoDeletionPolicy : undefined,
|
||||
config: config as MeetRoomConfig,
|
||||
roles: roomRoles,
|
||||
anonymous: anonymousConfig,
|
||||
accessUrl: `/room/${roomId}`,
|
||||
access: accessConfig,
|
||||
status: MeetRoomStatus.OPEN,
|
||||
rolesUpdatedAt: now,
|
||||
meetingEndAction: MeetingEndAction.NONE
|
||||
@ -284,24 +293,24 @@ export class RoomService {
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the anonymous access configuration of a specific meeting room.
|
||||
* Updates the access configuration of a specific meeting room.
|
||||
*
|
||||
* @param roomId - The unique identifier of the meeting room to update
|
||||
* @param anonymous - Partial anonymous config with the fields to update
|
||||
* @param access - Partial access config with the fields to update
|
||||
* @returns A Promise that resolves to the updated MeetRoom object
|
||||
*/
|
||||
async updateMeetRoomAnonymous(roomId: string, anonymous: MeetRoomAnonymousConfig): Promise<MeetRoom> {
|
||||
const room = await this.getMeetRoom(roomId, ['anonymous', 'status']);
|
||||
async updateMeetRoomAccess(roomId: string, access: MeetRoomAccessConfig): Promise<MeetRoom> {
|
||||
const room = await this.getMeetRoom(roomId, ['access', 'status']);
|
||||
|
||||
if (room.status === MeetRoomStatus.ACTIVE_MEETING) {
|
||||
throw errorRoomActiveMeeting(roomId);
|
||||
}
|
||||
|
||||
// Merge existing anonymous config with new anonymous config (partial update)
|
||||
const updatedAnonymous = merge({}, room.anonymous, anonymous);
|
||||
// Merge existing access config with new access config (partial update)
|
||||
const updatedAccess = merge({}, room.access, access);
|
||||
|
||||
return this.roomRepository.updatePartial(roomId, {
|
||||
anonymous: updatedAnonymous,
|
||||
access: updatedAccess,
|
||||
rolesUpdatedAt: Date.now()
|
||||
});
|
||||
}
|
||||
@ -870,9 +879,10 @@ export class RoomService {
|
||||
* @throws Error if room not found
|
||||
*/
|
||||
async isValidRoomSecret(roomId: string, secret: string): Promise<boolean> {
|
||||
const { anonymous } = await this.getMeetRoom(roomId, ['anonymous']);
|
||||
const { moderatorSecret, speakerSecret } = MeetRoomHelper.extractSecretsFromRoom(anonymous);
|
||||
return secret === moderatorSecret || secret === speakerSecret;
|
||||
const { access } = await this.getMeetRoom(roomId, ['access']);
|
||||
const { moderatorSecret, speakerSecret, recordingSecret } = MeetRoomHelper.extractSecretsFromRoom(access);
|
||||
const allSecrets = [moderatorSecret, speakerSecret, recordingSecret];
|
||||
return allSecrets.includes(secret);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@ -169,18 +169,23 @@ export const expectValidRoom = (
|
||||
expect(room.owner).toBeDefined();
|
||||
expect(room.roles).toBeDefined();
|
||||
|
||||
expect(room.anonymous).toBeDefined();
|
||||
expect(room.anonymous.moderator).toBeDefined();
|
||||
expect(room.anonymous.speaker).toBeDefined();
|
||||
expect(room.anonymous.moderator.enabled).toBeDefined();
|
||||
expect(room.anonymous.speaker.enabled).toBeDefined();
|
||||
expect(room.anonymous.moderator.accessUrl).toBeDefined();
|
||||
expect(room.anonymous.speaker.accessUrl).toBeDefined();
|
||||
expect(room.anonymous.moderator.accessUrl).toContain(room.roomId);
|
||||
expect(room.anonymous.speaker.accessUrl).toContain(room.roomId);
|
||||
|
||||
expect(room.accessUrl).toBeDefined();
|
||||
expect(room.accessUrl).toContain(room.roomId);
|
||||
expect(room.access).toBeDefined();
|
||||
expect(room.access.anonymous.moderator).toBeDefined();
|
||||
expect(room.access.anonymous.speaker).toBeDefined();
|
||||
expect(room.access.anonymous.recording).toBeDefined();
|
||||
expect(room.access.registered).toBeDefined();
|
||||
expect(room.access.anonymous.moderator.enabled).toBeDefined();
|
||||
expect(room.access.anonymous.speaker.enabled).toBeDefined();
|
||||
expect(room.access.anonymous.recording.enabled).toBeDefined();
|
||||
expect(room.access.registered.enabled).toBeDefined();
|
||||
expect(room.access.anonymous.moderator.url).toBeDefined();
|
||||
expect(room.access.anonymous.speaker.url).toBeDefined();
|
||||
expect(room.access.anonymous.recording.url).toBeDefined();
|
||||
expect(room.access.registered.url).toBeDefined();
|
||||
expect(room.access.anonymous.moderator.url).toContain(room.roomId);
|
||||
expect(room.access.anonymous.speaker.url).toContain(room.roomId);
|
||||
expect(room.access.anonymous.recording.url).toContain(room.roomId);
|
||||
expect(room.access.registered.url).toContain(room.roomId);
|
||||
|
||||
expect(room.status).toBeDefined();
|
||||
expect(room.status).toEqual(status || MeetRoomStatus.OPEN);
|
||||
|
||||
@ -6,7 +6,7 @@ import {
|
||||
MeetRecordingInfo,
|
||||
MeetRecordingStatus,
|
||||
MeetRoom,
|
||||
MeetRoomAnonymousConfig,
|
||||
MeetRoomAccessConfig,
|
||||
MeetRoomConfig,
|
||||
MeetRoomDeletionPolicyWithMeeting,
|
||||
MeetRoomDeletionPolicyWithRecordings,
|
||||
@ -547,13 +547,13 @@ export const updateRoomRoles = async (roomId: string, rolesConfig: MeetRoomRoles
|
||||
.send({ roles: rolesConfig });
|
||||
};
|
||||
|
||||
export const updateRoomAnonymousConfig = async (roomId: string, anonymousConfig: MeetRoomAnonymousConfig) => {
|
||||
export const updateRoomAccessConfig = async (roomId: string, accessConfig: MeetRoomAccessConfig) => {
|
||||
checkAppIsRunning();
|
||||
|
||||
return await request(app)
|
||||
.put(getFullPath(`${INTERNAL_CONFIG.API_BASE_PATH_V1}/rooms/${roomId}/anonymous`))
|
||||
.put(getFullPath(`${INTERNAL_CONFIG.API_BASE_PATH_V1}/rooms/${roomId}/access`))
|
||||
.set(INTERNAL_CONFIG.API_KEY_HEADER, MEET_ENV.INITIAL_API_KEY)
|
||||
.send({ anonymous: anonymousConfig });
|
||||
.send({ access: accessConfig });
|
||||
};
|
||||
|
||||
export const deleteRoom = async (
|
||||
|
||||
@ -51,7 +51,7 @@ export const setupSingleRoom = async (
|
||||
});
|
||||
|
||||
// Extract the room secrets and generate room member tokens
|
||||
const { moderatorSecret, speakerSecret } = MeetRoomHelper.extractSecretsFromRoom(room.anonymous);
|
||||
const { moderatorSecret, speakerSecret } = MeetRoomHelper.extractSecretsFromRoom(room.access);
|
||||
const [moderatorToken, speakerToken] = await Promise.all([
|
||||
generateRoomMemberToken(room.roomId, { secret: moderatorSecret, joinMeeting: false }),
|
||||
generateRoomMemberToken(room.roomId, { secret: speakerSecret, joinMeeting: false })
|
||||
|
||||
@ -15,7 +15,7 @@ import {
|
||||
resetUserPassword,
|
||||
sleep,
|
||||
startTestServer,
|
||||
updateRoomAnonymousConfig,
|
||||
updateRoomAccessConfig,
|
||||
updateRoomConfig,
|
||||
updateRoomMember,
|
||||
updateRoomRoles
|
||||
@ -478,7 +478,7 @@ describe('Token Validation Tests', () => {
|
||||
expect(response.body.message).toContain('Invalid token');
|
||||
});
|
||||
|
||||
it('should succeed when room anonymous config is updated after room member token issuance', async () => {
|
||||
it('should succeed when room access config is updated after room member token issuance', async () => {
|
||||
// Create a room with an external member
|
||||
const roomData = await setupSingleRoom();
|
||||
const roomMember = await setupRoomMember(roomData.room.roomId, {
|
||||
@ -492,11 +492,13 @@ describe('Token Validation Tests', () => {
|
||||
.set(INTERNAL_CONFIG.ROOM_MEMBER_TOKEN_HEADER, roomMember.memberToken);
|
||||
expect(initialResponse.status).toBe(200);
|
||||
|
||||
// Update the room anonymous configuration
|
||||
// Update the room access configuration
|
||||
await sleep('100ms'); // Small delay to ensure timestamp difference
|
||||
await updateRoomAnonymousConfig(roomData.room.roomId, {
|
||||
moderator: {
|
||||
enabled: false
|
||||
await updateRoomAccessConfig(roomData.room.roomId, {
|
||||
anonymous: {
|
||||
moderator: {
|
||||
enabled: false
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
@ -507,7 +509,7 @@ describe('Token Validation Tests', () => {
|
||||
expect(response.status).toBe(200);
|
||||
});
|
||||
|
||||
it('should fail when room anonymous config is updated after anonymous room member token issuance', async () => {
|
||||
it('should fail when room access config is updated after anonymous room member token issuance', async () => {
|
||||
// Create a room and generate an anonymous room member token
|
||||
const roomData = await setupSingleRoom();
|
||||
|
||||
@ -517,11 +519,13 @@ describe('Token Validation Tests', () => {
|
||||
.set(INTERNAL_CONFIG.ROOM_MEMBER_TOKEN_HEADER, roomData.moderatorToken);
|
||||
expect(initialResponse.status).toBe(200);
|
||||
|
||||
// Update the room's anonymous configuration
|
||||
// Update the room's access configuration
|
||||
await sleep('100ms'); // Small delay to ensure timestamp difference
|
||||
await updateRoomAnonymousConfig(roomData.room.roomId, {
|
||||
moderator: {
|
||||
enabled: false
|
||||
await updateRoomAccessConfig(roomData.room.roomId, {
|
||||
anonymous: {
|
||||
moderator: {
|
||||
enabled: false
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
@ -538,7 +542,7 @@ describe('Token Validation Tests', () => {
|
||||
// Create a room and generate an anonymous room member token
|
||||
const roomData = await setupSingleRoom();
|
||||
|
||||
// Update room config (not roles/permissions/anonymous)
|
||||
// Update room config (not roles/access)
|
||||
await sleep('100ms');
|
||||
await updateRoomConfig(roomData.room.roomId, {
|
||||
chat: { enabled: false }
|
||||
|
||||
@ -18,7 +18,7 @@ import {
|
||||
generateRoomMemberToken,
|
||||
generateRoomMemberTokenRequest,
|
||||
startTestServer,
|
||||
updateRoomAnonymousConfig,
|
||||
updateRoomAccessConfig,
|
||||
updateRoomRoles,
|
||||
updateRoomStatus
|
||||
} from '../../../helpers/request-helpers.js';
|
||||
@ -63,8 +63,10 @@ describe('Room Members API Tests', () => {
|
||||
describe('Generate Room Member Token Tests', () => {
|
||||
it('should generate anonymous moderator token when anonymous.moderator.enabled is true', async () => {
|
||||
// Enable anonymous moderator access
|
||||
await updateRoomAnonymousConfig(roomId, {
|
||||
moderator: { enabled: true }
|
||||
await updateRoomAccessConfig(roomId, {
|
||||
anonymous: {
|
||||
moderator: { enabled: true }
|
||||
}
|
||||
});
|
||||
|
||||
const response = await generateRoomMemberTokenRequest(roomId, { secret: roomData.moderatorSecret });
|
||||
@ -77,23 +79,29 @@ describe('Room Members API Tests', () => {
|
||||
|
||||
it('should fail to generate anonymous moderator token when anonymous.moderator.enabled is false', async () => {
|
||||
// Disable anonymous moderator access
|
||||
await updateRoomAnonymousConfig(roomId, {
|
||||
moderator: { enabled: false }
|
||||
await updateRoomAccessConfig(roomId, {
|
||||
anonymous: {
|
||||
moderator: { enabled: false }
|
||||
}
|
||||
});
|
||||
|
||||
const response = await generateRoomMemberTokenRequest(roomId, { secret: roomData.moderatorSecret });
|
||||
expect(response.status).toBe(403);
|
||||
|
||||
// Enable anonymous moderator access for further tests
|
||||
await updateRoomAnonymousConfig(roomId, {
|
||||
moderator: { enabled: true }
|
||||
await updateRoomAccessConfig(roomId, {
|
||||
anonymous: {
|
||||
moderator: { enabled: true }
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
it('should generate anonymous speaker token when anonymous.speaker.enabled is true', async () => {
|
||||
// Enable anonymous speaker access
|
||||
await updateRoomAnonymousConfig(roomId, {
|
||||
speaker: { enabled: true }
|
||||
await updateRoomAccessConfig(roomId, {
|
||||
anonymous: {
|
||||
speaker: { enabled: true }
|
||||
}
|
||||
});
|
||||
|
||||
const response = await generateRoomMemberTokenRequest(roomId, { secret: roomData.speakerSecret });
|
||||
@ -106,16 +114,20 @@ describe('Room Members API Tests', () => {
|
||||
|
||||
it('should fail to generate anonymous speaker token when anonymous.speaker.enabled is false', async () => {
|
||||
// Disable anonymous speaker access
|
||||
await updateRoomAnonymousConfig(roomId, {
|
||||
speaker: { enabled: false }
|
||||
await updateRoomAccessConfig(roomId, {
|
||||
anonymous: {
|
||||
speaker: { enabled: false }
|
||||
}
|
||||
});
|
||||
|
||||
const response = await generateRoomMemberTokenRequest(roomId, { secret: roomData.speakerSecret });
|
||||
expect(response.status).toBe(403);
|
||||
|
||||
// Enable anonymous speaker access for further tests
|
||||
await updateRoomAnonymousConfig(roomId, {
|
||||
speaker: { enabled: true }
|
||||
await updateRoomAccessConfig(roomId, {
|
||||
anonymous: {
|
||||
speaker: { enabled: true }
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
@ -98,7 +98,7 @@ describe('Expired Rooms GC Tests', () => {
|
||||
expect(response.body).toHaveProperty('meetingEndAction', 'delete');
|
||||
|
||||
// End the meeting
|
||||
const { moderatorSecret } = MeetRoomHelper.extractSecretsFromRoom(room.anonymous);
|
||||
const { moderatorSecret } = MeetRoomHelper.extractSecretsFromRoom(room.access);
|
||||
const moderatorToken = await generateRoomMemberToken(room.roomId, { secret: moderatorSecret });
|
||||
await endMeeting(room.roomId, moderatorToken);
|
||||
|
||||
|
||||
@ -100,17 +100,26 @@ const expectMigratedRoomToCurrentVersion = (migratedRoom: Record<string, unknown
|
||||
permissions: expect.any(Object)
|
||||
}
|
||||
},
|
||||
anonymous: {
|
||||
moderator: {
|
||||
enabled: true,
|
||||
accessUrl: `/room/${roomId}?secret=123456`
|
||||
access: {
|
||||
anonymous: {
|
||||
moderator: {
|
||||
enabled: true,
|
||||
url: `/room/${roomId}?secret=123456`
|
||||
},
|
||||
speaker: {
|
||||
enabled: true,
|
||||
url: `/room/${roomId}?secret=abcdef`
|
||||
},
|
||||
recording: {
|
||||
enabled: false,
|
||||
url: expect.stringContaining(`/room/${roomId}/recordings`)
|
||||
}
|
||||
},
|
||||
speaker: {
|
||||
registered: {
|
||||
enabled: true,
|
||||
accessUrl: `/room/${roomId}?secret=abcdef`
|
||||
url: `/room/${roomId}`
|
||||
}
|
||||
},
|
||||
accessUrl: `/room/${roomId}`,
|
||||
rolesUpdatedAt: expect.any(Number),
|
||||
status: MeetRoomStatus.OPEN,
|
||||
meetingEndAction: MeetingEndAction.NONE
|
||||
@ -259,17 +268,26 @@ describe('Room Schema Migrations', () => {
|
||||
}
|
||||
}
|
||||
},
|
||||
anonymous: {
|
||||
moderator: {
|
||||
enabled: true,
|
||||
accessUrl: '/room/room-v2?secret=123456'
|
||||
access: {
|
||||
anonymous: {
|
||||
moderator: {
|
||||
enabled: true,
|
||||
url: '/room/room-v2?secret=123456'
|
||||
},
|
||||
speaker: {
|
||||
enabled: true,
|
||||
url: '/room/room-v2?secret=abcdef'
|
||||
},
|
||||
recording: {
|
||||
enabled: false,
|
||||
url: expect.stringContaining('/room/room-v2/recordings')
|
||||
}
|
||||
},
|
||||
speaker: {
|
||||
registered: {
|
||||
enabled: true,
|
||||
accessUrl: '/room/room-v2?secret=abcdef'
|
||||
url: '/room/room-v2'
|
||||
}
|
||||
},
|
||||
accessUrl: '/room/room-v2',
|
||||
rolesUpdatedAt: expect.any(Number),
|
||||
status: MeetRoomStatus.OPEN,
|
||||
meetingEndAction: MeetingEndAction.NONE
|
||||
|
||||
@ -757,7 +757,7 @@ describe('Room API Security Tests', () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe('Update Room Anonymous Config Tests', () => {
|
||||
describe('Update Room Access Config Tests', () => {
|
||||
let roomData: RoomData;
|
||||
let roomId: string;
|
||||
let roomUsers: RoomTestUsers;
|
||||
@ -771,70 +771,70 @@ describe('Room API Security Tests', () => {
|
||||
|
||||
it('should succeed when request includes API key', async () => {
|
||||
const response = await request(app)
|
||||
.put(`${ROOMS_PATH}/${roomId}/anonymous`)
|
||||
.put(`${ROOMS_PATH}/${roomId}/access`)
|
||||
.set(INTERNAL_CONFIG.API_KEY_HEADER, MEET_ENV.INITIAL_API_KEY)
|
||||
.send({ anonymous: {} });
|
||||
.send({ access: {} });
|
||||
expect(response.status).toBe(200);
|
||||
});
|
||||
|
||||
it('should succeed when user is authenticated as ADMIN', async () => {
|
||||
const response = await request(app)
|
||||
.put(`${ROOMS_PATH}/${roomId}/anonymous`)
|
||||
.put(`${ROOMS_PATH}/${roomId}/access`)
|
||||
.set(INTERNAL_CONFIG.ACCESS_TOKEN_HEADER, testUsers.admin.accessToken)
|
||||
.send({ anonymous: {} });
|
||||
.send({ access: {} });
|
||||
expect(response.status).toBe(200);
|
||||
});
|
||||
|
||||
it('should succeed when user is authenticated as USER and is room owner', async () => {
|
||||
const response = await request(app)
|
||||
.put(`${ROOMS_PATH}/${roomId}/anonymous`)
|
||||
.put(`${ROOMS_PATH}/${roomId}/access`)
|
||||
.set(INTERNAL_CONFIG.ACCESS_TOKEN_HEADER, roomUsers.userOwner.accessToken)
|
||||
.send({ anonymous: {} });
|
||||
.send({ access: {} });
|
||||
expect(response.status).toBe(200);
|
||||
});
|
||||
|
||||
it('should fail when user is authenticated as USER and is room member', async () => {
|
||||
const response = await request(app)
|
||||
.put(`${ROOMS_PATH}/${roomId}/anonymous`)
|
||||
.put(`${ROOMS_PATH}/${roomId}/access`)
|
||||
.set(INTERNAL_CONFIG.ACCESS_TOKEN_HEADER, roomUsers.userMember.accessToken)
|
||||
.send({ anonymous: {} });
|
||||
.send({ access: {} });
|
||||
expect(response.status).toBe(403);
|
||||
});
|
||||
|
||||
it('should fail when user is authenticated as USER without access to the room', async () => {
|
||||
const response = await request(app)
|
||||
.put(`${ROOMS_PATH}/${roomId}/anonymous`)
|
||||
.put(`${ROOMS_PATH}/${roomId}/access`)
|
||||
.set(INTERNAL_CONFIG.ACCESS_TOKEN_HEADER, testUsers.user.accessToken)
|
||||
.send({ anonymous: {} });
|
||||
.send({ access: {} });
|
||||
expect(response.status).toBe(403);
|
||||
});
|
||||
|
||||
it('should fail when user is authenticated as ROOM_MEMBER and is room member', async () => {
|
||||
const response = await request(app)
|
||||
.put(`${ROOMS_PATH}/${roomId}/anonymous`)
|
||||
.put(`${ROOMS_PATH}/${roomId}/access`)
|
||||
.set(INTERNAL_CONFIG.ACCESS_TOKEN_HEADER, roomUsers.roomMember.accessToken)
|
||||
.send({ anonymous: {} });
|
||||
.send({ access: {} });
|
||||
expect(response.status).toBe(403);
|
||||
});
|
||||
|
||||
it('should fail when user is authenticated as ROOM_MEMBER without access to the room', async () => {
|
||||
const response = await request(app)
|
||||
.put(`${ROOMS_PATH}/${roomId}/anonymous`)
|
||||
.put(`${ROOMS_PATH}/${roomId}/access`)
|
||||
.set(INTERNAL_CONFIG.ACCESS_TOKEN_HEADER, testUsers.roomMember.accessToken)
|
||||
.send({ anonymous: {} });
|
||||
.send({ access: {} });
|
||||
expect(response.status).toBe(403);
|
||||
});
|
||||
|
||||
it('should fail when user is not authenticated', async () => {
|
||||
const response = await request(app).put(`${ROOMS_PATH}/${roomId}/anonymous`).send({ anonymous: {} });
|
||||
const response = await request(app).put(`${ROOMS_PATH}/${roomId}/access`).send({ access: {} });
|
||||
expect(response.status).toBe(401);
|
||||
});
|
||||
|
||||
it('should fail when using room member token', async () => {
|
||||
const response = await request(app)
|
||||
.put(`${ROOMS_PATH}/${roomId}/anonymous`)
|
||||
.put(`${ROOMS_PATH}/${roomId}/access`)
|
||||
.set(INTERNAL_CONFIG.ROOM_MEMBER_TOKEN_HEADER, roomData.moderatorToken)
|
||||
.send({ anonymous: {} });
|
||||
.send({ access: {} });
|
||||
expect(response.status).toBe(401);
|
||||
});
|
||||
});
|
||||
|
||||
@ -93,7 +93,7 @@ export class MeetingContextService {
|
||||
setMeetRoom(room: MeetRoom): void {
|
||||
this._meetRoom.set(room);
|
||||
this.setRoomId(room.roomId);
|
||||
this.setMeetingUrl(room.accessUrl);
|
||||
this.setMeetingUrl(room.access.registered.url);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@ -156,7 +156,7 @@ export class MeetingLobbyService {
|
||||
|
||||
const [room] = await Promise.all([
|
||||
this.roomService.getRoom(roomId, {
|
||||
fields: ['roomId', 'roomName', 'status', 'config', 'accessUrl', 'anonymous'],
|
||||
fields: ['roomId', 'roomName', 'status', 'config', 'access'],
|
||||
extraFields: ['config']
|
||||
}),
|
||||
this.setBackButtonText(),
|
||||
|
||||
@ -21,7 +21,7 @@ export class MeetingService {
|
||||
* Copies the meeting speaker link to the clipboard.
|
||||
*/
|
||||
copyMeetingSpeakerLink(room: MeetRoom): void {
|
||||
const speakerLink = room.anonymous.speaker.accessUrl;
|
||||
const speakerLink = room.access.anonymous.speaker.url;
|
||||
this.clipboard.copy(speakerLink);
|
||||
this.notificationService.showSnackbar('Speaker link copied to clipboard');
|
||||
}
|
||||
|
||||
@ -201,14 +201,14 @@ export class RoomDetailComponent implements OnInit {
|
||||
const room = this.room();
|
||||
if (!room) return;
|
||||
|
||||
window.open(room.accessUrl, '_blank');
|
||||
window.open(room.access.registered.url, '_blank');
|
||||
}
|
||||
|
||||
copyAccessLink() {
|
||||
const room = this.room();
|
||||
if (!room) return;
|
||||
|
||||
this.clipboard.copy(room.accessUrl);
|
||||
this.clipboard.copy(room.access.registered.url);
|
||||
this.notificationService.showSnackbar('Access link copied to clipboard');
|
||||
}
|
||||
|
||||
|
||||
@ -137,10 +137,10 @@ export class RoomWizardComponent implements OnInit {
|
||||
async createRoomBasic(roomName?: string) {
|
||||
try {
|
||||
// Create room with basic config including e2ee: false (default settings)
|
||||
const { accessUrl } = await this.roomService.createRoom({ roomName }, { fields: ['accessUrl'] });
|
||||
const { access } = await this.roomService.createRoom({ roomName }, { fields: ['access'] });
|
||||
|
||||
// Extract the path from the access URL and navigate to it
|
||||
const url = new URL(accessUrl);
|
||||
const url = new URL(access.registered.url);
|
||||
const path = url.pathname;
|
||||
await this.navigationService.redirectTo(path);
|
||||
} catch (error) {
|
||||
@ -168,10 +168,10 @@ export class RoomWizardComponent implements OnInit {
|
||||
this.notificationService.showSnackbar('Room updated successfully');
|
||||
} else {
|
||||
// Create new room
|
||||
const { accessUrl } = await this.roomService.createRoom(roomOptions, { fields: ['accessUrl'] });
|
||||
const { access } = await this.roomService.createRoom(roomOptions, { fields: ['access'] });
|
||||
|
||||
// Extract the path from the access URL and navigate to it
|
||||
const url = new URL(accessUrl);
|
||||
const url = new URL(access.registered.url);
|
||||
const path = url.pathname;
|
||||
await this.navigationService.redirectTo(path);
|
||||
}
|
||||
|
||||
@ -26,37 +26,102 @@ export const PERMISSION_GROUPS: PermissionGroup[] = [
|
||||
label: 'Meeting',
|
||||
icon: 'groups',
|
||||
permissions: [
|
||||
{ key: 'canJoinMeeting', label: 'Can join meeting', description: 'Allow joining the meeting', icon: 'login' },
|
||||
{ key: 'canEndMeeting', label: 'Can end meeting', description: 'Allow ending the meeting for all participants', icon: 'meeting_room' },
|
||||
{ key: 'canMakeModerator', label: 'Can make moderator', description: 'Allow promoting participants to moderator role', icon: 'manage_accounts' },
|
||||
{ key: 'canKickParticipants', label: 'Can kick participants', description: 'Allow removing participants from the meeting', icon: 'person_remove' },
|
||||
{ key: 'canShareAccessLinks', label: 'Can share access links', description: 'Allow sharing invite links with others', icon: 'link' }
|
||||
{
|
||||
key: 'canJoinMeeting',
|
||||
label: 'Can join meeting',
|
||||
description: 'Allow joining the meeting',
|
||||
icon: 'login'
|
||||
},
|
||||
{
|
||||
key: 'canEndMeeting',
|
||||
label: 'Can end meeting',
|
||||
description: 'Allow ending the meeting for all participants',
|
||||
icon: 'meeting_room'
|
||||
},
|
||||
{
|
||||
key: 'canMakeModerator',
|
||||
label: 'Can make moderator',
|
||||
description: 'Allow promoting participants to moderator role',
|
||||
icon: 'manage_accounts'
|
||||
},
|
||||
{
|
||||
key: 'canKickParticipants',
|
||||
label: 'Can kick participants',
|
||||
description: 'Allow removing participants from the meeting',
|
||||
icon: 'person_remove'
|
||||
},
|
||||
{
|
||||
key: 'canShareAccessLinks',
|
||||
label: 'Can share access links',
|
||||
description: 'Allow sharing invite links with others',
|
||||
icon: 'link'
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
label: 'Media',
|
||||
icon: 'perm_media',
|
||||
permissions: [
|
||||
{ key: 'canPublishVideo', label: 'Can publish video', description: 'Allow sharing camera video', icon: 'videocam' },
|
||||
{ key: 'canPublishAudio', label: 'Can publish audio', description: 'Allow sharing microphone audio', icon: 'mic' },
|
||||
{ key: 'canShareScreen', label: 'Can share screen', description: 'Allow sharing desktop or browser tabs', icon: 'screen_share' },
|
||||
{ key: 'canChangeVirtualBackground', label: 'Can change virtual background', description: 'Allow changing the virtual background', icon: 'background_replace' }
|
||||
{
|
||||
key: 'canPublishVideo',
|
||||
label: 'Can publish video',
|
||||
description: 'Allow sharing camera video',
|
||||
icon: 'videocam'
|
||||
},
|
||||
{
|
||||
key: 'canPublishAudio',
|
||||
label: 'Can publish audio',
|
||||
description: 'Allow sharing microphone audio',
|
||||
icon: 'mic'
|
||||
},
|
||||
{
|
||||
key: 'canShareScreen',
|
||||
label: 'Can share screen',
|
||||
description: 'Allow sharing desktop or browser tabs',
|
||||
icon: 'screen_share'
|
||||
},
|
||||
{
|
||||
key: 'canChangeVirtualBackground',
|
||||
label: 'Can change virtual background',
|
||||
description: 'Allow changing the virtual background',
|
||||
icon: 'background_replace'
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
label: 'Recordings',
|
||||
icon: 'video_library',
|
||||
permissions: [
|
||||
{ key: 'canRecord', label: 'Can record', description: 'Allow starting and stopping recordings', icon: 'fiber_manual_record' },
|
||||
{ key: 'canRetrieveRecordings', label: 'Can retrieve recordings', description: 'Allow listing and playing recordings', icon: 'play_circle' },
|
||||
{ key: 'canDeleteRecordings', label: 'Can delete recordings', description: 'Allow deleting recordings', icon: 'delete' }
|
||||
{
|
||||
key: 'canRecord',
|
||||
label: 'Can record',
|
||||
description: 'Allow starting and stopping recordings',
|
||||
icon: 'fiber_manual_record'
|
||||
},
|
||||
{
|
||||
key: 'canRetrieveRecordings',
|
||||
label: 'Can retrieve recordings',
|
||||
description: 'Allow listing and playing recordings',
|
||||
icon: 'play_circle'
|
||||
},
|
||||
{
|
||||
key: 'canDeleteRecordings',
|
||||
label: 'Can delete recordings',
|
||||
description: 'Allow deleting recordings',
|
||||
icon: 'delete'
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
label: 'Chat',
|
||||
icon: 'chat',
|
||||
permissions: [
|
||||
{ key: 'canReadChat', label: 'Can read chat', description: 'Allow reading chat messages', icon: 'visibility' },
|
||||
{
|
||||
key: 'canReadChat',
|
||||
label: 'Can read chat',
|
||||
description: 'Allow reading chat messages',
|
||||
icon: 'visibility'
|
||||
},
|
||||
{ key: 'canWriteChat', label: 'Can write chat', description: 'Allow sending chat messages', icon: 'edit' }
|
||||
]
|
||||
}
|
||||
@ -107,9 +172,11 @@ export class RolePermissionsComponent implements OnDestroy {
|
||||
moderator: { permissions: buildPermissions(formValue.moderator) },
|
||||
speaker: { permissions: buildPermissions(formValue.speaker) }
|
||||
},
|
||||
anonymous: {
|
||||
moderator: { enabled: formValue.moderator.anonymousEnabled ?? false },
|
||||
speaker: { enabled: formValue.speaker.anonymousEnabled ?? false }
|
||||
access: {
|
||||
anonymous: {
|
||||
moderator: { enabled: formValue.moderator.anonymousEnabled ?? false },
|
||||
speaker: { enabled: formValue.speaker.anonymousEnabled ?? false }
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@ -206,8 +206,8 @@ export class RoomsComponent implements OnInit {
|
||||
}
|
||||
}
|
||||
|
||||
private openRoom({ accessUrl }: MeetRoom) {
|
||||
window.open(accessUrl, '_blank');
|
||||
private openRoom({ access }: MeetRoom) {
|
||||
window.open(access.registered.url, '_blank');
|
||||
}
|
||||
|
||||
private async editRoomConfig(room: MeetRoom) {
|
||||
@ -219,13 +219,13 @@ export class RoomsComponent implements OnInit {
|
||||
}
|
||||
}
|
||||
|
||||
private copyModeratorLink({ anonymous }: MeetRoom) {
|
||||
this.clipboard.copy(anonymous.moderator.accessUrl);
|
||||
private copyModeratorLink({ access }: MeetRoom) {
|
||||
this.clipboard.copy(access.anonymous.moderator.url);
|
||||
this.notificationService.showSnackbar('Moderator link copied to clipboard');
|
||||
}
|
||||
|
||||
private copySpeakerLink({ anonymous }: MeetRoom) {
|
||||
this.clipboard.copy(anonymous.speaker.accessUrl);
|
||||
private copySpeakerLink({ access }: MeetRoom) {
|
||||
this.clipboard.copy(access.anonymous.speaker.url);
|
||||
this.notificationService.showSnackbar('Speaker link copied to clipboard');
|
||||
}
|
||||
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
import { inject, Injectable } from '@angular/core';
|
||||
import {
|
||||
MeetRoom,
|
||||
MeetRoomAnonymousConfig,
|
||||
MeetRoomAccessConfig,
|
||||
MeetRoomConfig,
|
||||
MeetRoomDeletionPolicyWithMeeting,
|
||||
MeetRoomDeletionPolicyWithRecordings,
|
||||
@ -167,15 +167,15 @@ export class RoomService {
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the anonymous access configuration of a room.
|
||||
* Updates the access configuration of a room.
|
||||
*
|
||||
* @param roomId - The unique identifier of the room
|
||||
* @param anonymousConfig - The new anonymous access configuration to be set
|
||||
* @returns A promise that resolves when the anonymous access configuration has been updated
|
||||
* @param accessConfig - The new access configuration to be set
|
||||
* @returns A promise that resolves when the access configuration has been updated
|
||||
*/
|
||||
async updateRoomAnonymous(roomId: string, anonymousConfig: MeetRoomAnonymousConfig): Promise<void> {
|
||||
const path = `${this.ROOMS_API}/${roomId}/anonymous`;
|
||||
return this.httpService.putRequest(path, { anonymous: anonymousConfig });
|
||||
async updateRoomAccess(roomId: string, accessConfig: MeetRoomAccessConfig): Promise<void> {
|
||||
const path = `${this.ROOMS_API}/${roomId}/access`;
|
||||
return this.httpService.putRequest(path, { access: accessConfig });
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@ -238,13 +238,13 @@ export class RoomWizardStateService {
|
||||
isVisible: true,
|
||||
formGroup: this.formBuilder.group({
|
||||
moderator: this.formBuilder.group({
|
||||
anonymousEnabled: initialRoomOptions.anonymous?.moderator?.enabled ?? false,
|
||||
anonymousEnabled: initialRoomOptions.access?.anonymous?.moderator?.enabled ?? false,
|
||||
...this.buildPermissionsFormConfig(
|
||||
initialRoomOptions.roles?.moderator?.permissions ?? DEFAULT_MODERATOR_PERMISSIONS
|
||||
)
|
||||
}),
|
||||
speaker: this.formBuilder.group({
|
||||
anonymousEnabled: initialRoomOptions.anonymous?.speaker?.enabled ?? false,
|
||||
anonymousEnabled: initialRoomOptions.access?.anonymous?.speaker?.enabled ?? false,
|
||||
...this.buildPermissionsFormConfig(
|
||||
initialRoomOptions.roles?.speaker?.permissions ?? DEFAULT_SPEAKER_PERMISSIONS
|
||||
)
|
||||
@ -298,10 +298,7 @@ export class RoomWizardStateService {
|
||||
}
|
||||
}
|
||||
|
||||
private mergeRoomDetailsData(
|
||||
currentOptions: MeetRoomOptions,
|
||||
stepData: Partial<MeetRoomOptions>
|
||||
): MeetRoomOptions {
|
||||
private mergeRoomDetailsData(currentOptions: MeetRoomOptions, stepData: Partial<MeetRoomOptions>): MeetRoomOptions {
|
||||
return {
|
||||
...currentOptions,
|
||||
...('roomName' in stepData ? { roomName: stepData.roomName } : {}),
|
||||
@ -310,10 +307,7 @@ export class RoomWizardStateService {
|
||||
};
|
||||
}
|
||||
|
||||
private mergeRecordingData(
|
||||
currentOptions: MeetRoomOptions,
|
||||
stepData: Partial<MeetRoomOptions>
|
||||
): MeetRoomOptions {
|
||||
private mergeRecordingData(currentOptions: MeetRoomOptions, stepData: Partial<MeetRoomOptions>): MeetRoomOptions {
|
||||
return {
|
||||
...currentOptions,
|
||||
config: this.buildMergedConfig(currentOptions.config, {
|
||||
@ -353,16 +347,29 @@ export class RoomWizardStateService {
|
||||
}
|
||||
}
|
||||
},
|
||||
anonymous: {
|
||||
moderator: {
|
||||
enabled:
|
||||
stepData.anonymous?.moderator?.enabled ??
|
||||
currentOptions.anonymous?.moderator?.enabled ??
|
||||
false
|
||||
access: {
|
||||
anonymous: {
|
||||
moderator: {
|
||||
enabled:
|
||||
stepData.access?.anonymous?.moderator?.enabled ??
|
||||
currentOptions.access?.anonymous?.moderator?.enabled ??
|
||||
false
|
||||
},
|
||||
speaker: {
|
||||
enabled:
|
||||
stepData.access?.anonymous?.speaker?.enabled ??
|
||||
currentOptions.access?.anonymous?.speaker?.enabled ??
|
||||
false
|
||||
},
|
||||
recording: {
|
||||
enabled:
|
||||
stepData.access?.anonymous?.recording?.enabled ??
|
||||
currentOptions.access?.anonymous?.recording?.enabled ??
|
||||
false
|
||||
}
|
||||
},
|
||||
speaker: {
|
||||
enabled:
|
||||
stepData.anonymous?.speaker?.enabled ?? currentOptions.anonymous?.speaker?.enabled ?? false
|
||||
registered: {
|
||||
enabled: stepData.access?.registered?.enabled ?? currentOptions.access?.registered?.enabled ?? true
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@ -38,13 +38,9 @@ export interface MeetRoom {
|
||||
*/
|
||||
roles: MeetRoomRoles;
|
||||
/**
|
||||
* Anonymous access configuration for the room. See {@link MeetRoomAnonymous} for details.
|
||||
* Access configuration for the room. See {@link MeetRoomAccess} for details.
|
||||
*/
|
||||
anonymous: MeetRoomAnonymous;
|
||||
/**
|
||||
* General access URL for registered users with access to the room.
|
||||
*/
|
||||
accessUrl: string;
|
||||
access: MeetRoomAccess;
|
||||
/**
|
||||
* Status of the room. See {@link MeetRoomStatus} for details.
|
||||
*/
|
||||
@ -73,17 +69,63 @@ export interface MeetRoomRoles {
|
||||
}
|
||||
|
||||
/**
|
||||
* Anonymous access configuration for a room.
|
||||
* Access configuration for a room.
|
||||
*/
|
||||
export interface MeetRoomAccess {
|
||||
/**
|
||||
* Anonymous users access configuration for the room. See {@link MeetRoomAnonymousUsers} for details.
|
||||
*/
|
||||
anonymous: MeetRoomAnonymousUsers;
|
||||
/**
|
||||
* Registered users access configuration for the room. See {@link MeetRoomRegisteredUsers} for details.
|
||||
*/
|
||||
registered: MeetRoomRegisteredUsers;
|
||||
}
|
||||
|
||||
/**
|
||||
* Access configuration for registered users in a room.
|
||||
*/
|
||||
export interface MeetRoomRegisteredUsers {
|
||||
/**
|
||||
* Indicates if all registered users can access the room.
|
||||
* When enabled, any registered user can join, including non-members.
|
||||
* Room owners and administrators always have access regardless of this setting.
|
||||
*/
|
||||
enabled: boolean;
|
||||
/**
|
||||
* General access URL for registered users with access to the room.
|
||||
*/
|
||||
url: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Access configuration for anonymous users in a room.
|
||||
* Defines which roles have anonymous access enabled and their access URLs.
|
||||
*/
|
||||
export interface MeetRoomAnonymous {
|
||||
export interface MeetRoomAnonymousUsers {
|
||||
/**
|
||||
* Indicates if anonymous access is enabled for the moderator role in the room.
|
||||
* If true, anonymous users with moderator permissions can access the room using the provided URL.
|
||||
*/
|
||||
moderator: {
|
||||
enabled: boolean;
|
||||
accessUrl: string;
|
||||
url: string;
|
||||
};
|
||||
/**
|
||||
* Indicates if anonymous access is enabled for the speaker role in the room.
|
||||
* If true, anonymous users with speaker permissions can access the room using the provided URL.
|
||||
*/
|
||||
speaker: {
|
||||
enabled: boolean;
|
||||
accessUrl: string;
|
||||
url: string;
|
||||
};
|
||||
/**
|
||||
* Indicates if anonymous access is enabled for recordings of the room.
|
||||
* If true, anonymous users can access the recordings using the provided URL.
|
||||
*/
|
||||
recording: {
|
||||
enabled: boolean;
|
||||
url: string;
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@ -27,9 +27,9 @@ export interface MeetRoomOptions {
|
||||
*/
|
||||
roles?: MeetRoomRolesConfig;
|
||||
/**
|
||||
* Anonymous access configuration for the room. See {@link MeetRoomAnonymousConfig} for details.
|
||||
* Access configuration for the room. See {@link MeetRoomAccessConfig} for details.
|
||||
*/
|
||||
anonymous?: MeetRoomAnonymousConfig;
|
||||
access?: MeetRoomAccessConfig;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -46,14 +46,22 @@ export interface MeetRoomRolesConfig {
|
||||
}
|
||||
|
||||
/**
|
||||
* Anonymous access configuration for creating/updating a room.
|
||||
* Access configuration for creating/updating a room.
|
||||
* Only includes enabled flags.
|
||||
*/
|
||||
export interface MeetRoomAnonymousConfig {
|
||||
moderator?: {
|
||||
enabled: boolean;
|
||||
export interface MeetRoomAccessConfig {
|
||||
anonymous?: {
|
||||
moderator?: {
|
||||
enabled: boolean;
|
||||
};
|
||||
speaker?: {
|
||||
enabled: boolean;
|
||||
};
|
||||
recording?: {
|
||||
enabled: boolean;
|
||||
};
|
||||
};
|
||||
speaker?: {
|
||||
registered?: {
|
||||
enabled: boolean;
|
||||
};
|
||||
}
|
||||
|
||||
@ -14,8 +14,7 @@ export const MEET_ROOM_FIELDS = [
|
||||
'creationDate',
|
||||
'config',
|
||||
'roles',
|
||||
'anonymous',
|
||||
'accessUrl',
|
||||
'access',
|
||||
'status',
|
||||
'rolesUpdatedAt',
|
||||
'meetingEndAction',
|
||||
@ -57,7 +56,7 @@ export type MeetRoomSortField = (typeof MEET_ROOM_SORT_FIELDS)[number];
|
||||
* Sensitive fields of a MeetRoom that require specific permissions to be viewed.
|
||||
*/
|
||||
export const SENSITIVE_ROOM_FIELDS_BY_PERMISSION: Partial<Record<keyof MeetRoomMemberPermissions, MeetRoomField[]>> = {
|
||||
canShareAccessLinks: ['anonymous']
|
||||
canShareAccessLinks: ['access']
|
||||
};
|
||||
|
||||
export const SENSITIVE_ROOM_FIELDS_ENTRIES = Object.entries(SENSITIVE_ROOM_FIELDS_BY_PERMISSION) as ReadonlyArray<
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user