Rename publisher role to speaker across the application
This commit is contained in:
parent
69b5bf3071
commit
1161f1bb21
@ -57,51 +57,53 @@ openvidu-appdata/
|
||||
### Directory Descriptions
|
||||
|
||||
#### **Global Preferences** (`global-preferences.json`)
|
||||
|
||||
Contains system-wide settings and configurations for the OpenVidu Meet application, such as default recording settings, UI preferences, and feature toggles.
|
||||
|
||||
#### **Users** (`users/`)
|
||||
|
||||
Stores user account information in individual JSON files. Each file is named using the username (e.g., `admin.json`) and contains user-specific data including authentication details, permissions, and preferences.
|
||||
|
||||
#### **Rooms** (`rooms/`)
|
||||
|
||||
Contains room configuration and metadata. Each room is stored in its own directory named after the room ID, containing:
|
||||
|
||||
- `room-123.json`: Room configuration, settings, and metadata
|
||||
|
||||
#### **Recordings** (`recordings/`)
|
||||
|
||||
The recordings directory is organized into several subdirectories to manage different aspects of recorded content:
|
||||
|
||||
- **Recording Files** (`room-123/`): Contains the actual video files with naming convention `room-123--{uid}.mp4`
|
||||
|
||||
- **Metadata** (`.metadata/room-123/{egressId}/{uid}.json`): Stores recording metadata including:
|
||||
- Recording duration and timestamps
|
||||
- Participant information
|
||||
- Quality settings and technical specifications
|
||||
- File size and format details
|
||||
- Recording duration and timestamps
|
||||
- Participant information
|
||||
- Quality settings and technical specifications
|
||||
- File size and format details
|
||||
|
||||
- **Secrets** (`.secrets/room-123/{egressId}/{uid}.json`): Contains sensitive recording-related data such as:
|
||||
- Encryption keys
|
||||
- Access tokens
|
||||
- Security credentials
|
||||
- Encryption keys
|
||||
- Access tokens
|
||||
- Security credentials
|
||||
|
||||
- **Room Metadata** (`.room_metadata/room-123/room_metadata.json`): Stores room-level information for recordings including:
|
||||
- Room name and description
|
||||
- Recording session details
|
||||
- Participant list and roles
|
||||
- Room name and description
|
||||
- Recording session details
|
||||
- Participant list and roles
|
||||
|
||||
### Recording Identifier Format
|
||||
|
||||
Recordings use a composite identifier format: `recordingId: room-123--{egressId}--{uid}`
|
||||
|
||||
Where:
|
||||
|
||||
- `room-123`: The room identifier
|
||||
- `{egressId}`: LiveKit egress process identifier
|
||||
- `{uid}`: Unique recording session identifier
|
||||
|
||||
This naming convention ensures uniqueness and provides traceability between the recording file, its metadata, and the originating room session.
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## Recordings
|
||||
|
||||
The recording feature is based on the following key concepts:
|
||||
@ -112,7 +114,6 @@ The recording feature is based on the following key concepts:
|
||||
2. **Lock lifetime**:
|
||||
The lock has not lifetime. It is not automatically released after a certain period. Instead, it remains active until the recording is manually stopped and an `egress_ended` webhook is received, or when the room meeting ends. This design choice allows for flexibility in managing recordings, as the lock can be held for an extended duration if needed. However, it also means that care must be taken to ensure that the lock is released appropriately to avoid blocking future recording attempts. (see **Failure handling** below).
|
||||
|
||||
|
||||
```mermaid
|
||||
flowchart TD
|
||||
A["Start New Recording Request"] --> B{"Can room be recorded?"}
|
||||
@ -165,4 +166,3 @@ graph TD;
|
||||
M -->|No more rooms| N[Process completed]
|
||||
|
||||
```
|
||||
|
||||
|
||||
@ -3,10 +3,10 @@ in: header
|
||||
description: |
|
||||
The role of the participant in the meeting. It can be one of the following values:
|
||||
- `moderator`: Can manage the room and its participants.
|
||||
- `publisher`: Can publish media streams to the room.
|
||||
|
||||
- `speaker`: Can publish media streams to the room.
|
||||
|
||||
This is required to distinguish roles when multiple are present in the participant token
|
||||
required: true
|
||||
schema:
|
||||
type: string
|
||||
enum: ['moderator', 'publisher']
|
||||
enum: ['moderator', 'speaker']
|
||||
|
||||
@ -19,7 +19,7 @@ content:
|
||||
canRecord: true
|
||||
canChat: true
|
||||
canChangeVirtualBackground: true
|
||||
- role: 'publisher'
|
||||
- role: 'speaker'
|
||||
permissions:
|
||||
livekit:
|
||||
roomJoin: true
|
||||
|
||||
@ -18,8 +18,8 @@ content:
|
||||
enabled: false
|
||||
virtualBackgroundPreferences:
|
||||
enabled: true
|
||||
moderatorURL: 'http://localhost:6080/room/room-123?secret=123456'
|
||||
publisherURL: 'http://localhost:6080/room/room-123?secret=654321'
|
||||
moderatorRoomURL: 'http://localhost:6080/room/room-123?secret=123456'
|
||||
speakerRoomURL: 'http://localhost:6080/room/room-123?secret=654321'
|
||||
|
||||
fields=roomId:
|
||||
summary: Response with only the roomId
|
||||
@ -41,8 +41,8 @@ content:
|
||||
virtualBackgroundPreferences:
|
||||
enabled: true
|
||||
|
||||
fields=moderatorURL,publisherURL:
|
||||
summary: Response containing only moderator and publisher URLs
|
||||
fields=moderatorRoomURL,speakerRoomURL:
|
||||
summary: Response containing only moderator and speaker URLs
|
||||
value:
|
||||
moderatorURL: 'http://localhost:6080/room/room-123?secret=123456'
|
||||
publisherURL: 'http://localhost:6080/room/room-123?secret=654321'
|
||||
moderatorRoomURL: 'http://localhost:6080/room/room-123?secret=123456'
|
||||
speakerRoomURL: 'http://localhost:6080/room/room-123?secret=654321'
|
||||
|
||||
@ -27,8 +27,8 @@ content:
|
||||
enabled: false
|
||||
virtualBackgroundPreferences:
|
||||
enabled: true
|
||||
moderatorURL: 'http://localhost:6080/room/room-123?secret=123456'
|
||||
publisherURL: 'http://localhost:6080/room/room-123?secret=654321'
|
||||
moderatorRoomURL: 'http://localhost:6080/room/room-123?secret=123456'
|
||||
speakerRoomURL: 'http://localhost:6080/room/room-123?secret=654321'
|
||||
- roomId: 'room-456'
|
||||
roomName: 'room'
|
||||
creationDate: 1620001000000
|
||||
@ -40,8 +40,8 @@ content:
|
||||
enabled: true
|
||||
virtualBackgroundPreferences:
|
||||
enabled: false
|
||||
moderatorURL: 'http://localhost:6080/room/room-456?secret=789012'
|
||||
publisherURL: 'http://localhost:6080/room/room-456?secret=210987'
|
||||
moderatorRoomURL: 'http://localhost:6080/room/room-456?secret=789012'
|
||||
speakerRoomURL: 'http://localhost:6080/room/room-456?secret=210987'
|
||||
pagination:
|
||||
isTruncated: false
|
||||
maxItems: 10
|
||||
@ -86,14 +86,14 @@ content:
|
||||
nextPageToken: 'abc123'
|
||||
maxItems: 10
|
||||
|
||||
fields=moderatorURL,publisherURL:
|
||||
summary: Response containing only moderator and publisher URLs
|
||||
fields=moderatorRoomURL,speakerRoomURL:
|
||||
summary: Response containing only moderator and speaker URLs
|
||||
value:
|
||||
rooms:
|
||||
- moderatorURL: 'http://localhost:6080/room/room-123?secret=123456'
|
||||
publisherURL: 'http://localhost:6080/room/room-123?secret=654321'
|
||||
- moderatorURL: 'http://localhost:6080/room/room-456?secret=789012'
|
||||
publisherURL: 'http://localhost:6080/room/room-456?secret=210987'
|
||||
- moderatorRoomURL: 'http://localhost:6080/room/room-123?secret=123456'
|
||||
speakerRoomURL: 'http://localhost:6080/room/room-123?secret=654321'
|
||||
- moderatorRoomURL: 'http://localhost:6080/room/room-456?secret=789012'
|
||||
speakerRoomURL: 'http://localhost:6080/room/room-456?secret=210987'
|
||||
pagination:
|
||||
isTruncated: false
|
||||
maxItems: 10
|
||||
|
||||
@ -2,12 +2,12 @@ type: object
|
||||
properties:
|
||||
role:
|
||||
type: string
|
||||
enum: ['moderator', 'publisher']
|
||||
enum: ['moderator', 'speaker']
|
||||
description: |
|
||||
A role that a participant can have in a room.
|
||||
The role determines the permissions of the participant in the room.
|
||||
- `moderator`: Can manage the room and its participants.
|
||||
- `publisher`: Can publish media streams to the room.
|
||||
- `speaker`: Can publish media streams to the room.
|
||||
example: 'moderator'
|
||||
permissions:
|
||||
type: object
|
||||
|
||||
@ -31,14 +31,14 @@ MeetRecordingPreferences:
|
||||
enum:
|
||||
- admin
|
||||
- admin-moderator
|
||||
- admin-moderator-publisher
|
||||
default: admin-moderator-publisher
|
||||
example: admin-moderator-publisher
|
||||
- admin-moderator-speaker
|
||||
default: admin-moderator-speaker
|
||||
example: admin-moderator-speaker
|
||||
description: |
|
||||
Defines who can access the recording. Options are:
|
||||
- `admin`: Only administrators can access the recording.
|
||||
- `admin-moderator`: Administrators and moderators can access the recording.
|
||||
- `admin-moderator-publisher`: Administrators, moderators and publishers can access the recording.
|
||||
- `admin-moderator-speaker`: Administrators, moderators and speakers can access the recording.
|
||||
MeetVirtualBackgroundPreferences:
|
||||
type: object
|
||||
properties:
|
||||
|
||||
@ -39,15 +39,15 @@ properties:
|
||||
# description: >
|
||||
# The maximum number of participants allowed in the room. If the number of participants exceeds
|
||||
# this limit, new participants will not be allowed to join.
|
||||
moderatorURL:
|
||||
moderatorRoomURL:
|
||||
type: string
|
||||
example: 'http://localhost:6080/room/room-123?secret=123456'
|
||||
description: >
|
||||
The URL for the moderator participants to join the room. The moderator role has special permissions to manage the
|
||||
room and participants.
|
||||
publisherURL:
|
||||
speakerRoomURL:
|
||||
type: string
|
||||
example: 'http://localhost:6080/room/room-123?secret=654321'
|
||||
description: >
|
||||
The URL for the publisher participants to join the room. The publisher role has permissions to publish audio and
|
||||
The URL for the speaker participants to join the room. The speaker role has permissions to publish audio and
|
||||
video streams to the room.
|
||||
|
||||
427
backend/package-lock.json
generated
427
backend/package-lock.json
generated
@ -4087,12 +4087,12 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@smithy/abort-controller": {
|
||||
"version": "4.0.4",
|
||||
"resolved": "https://registry.npmjs.org/@smithy/abort-controller/-/abort-controller-4.0.4.tgz",
|
||||
"integrity": "sha512-gJnEjZMvigPDQWHrW3oPrFhQtkrgqBkyjj3pCIdF3A5M6vsZODG93KNlfJprv6bp4245bdT32fsHK4kkH3KYDA==",
|
||||
"version": "4.0.5",
|
||||
"resolved": "https://registry.npmjs.org/@smithy/abort-controller/-/abort-controller-4.0.5.tgz",
|
||||
"integrity": "sha512-jcrqdTQurIrBbUm4W2YdLVMQDoL0sA9DTxYd2s+R/y+2U9NLOP7Xf/YqfSg1FZhlZIYEnvk2mwbyvIfdLEPo8g==",
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"@smithy/types": "^4.3.1",
|
||||
"@smithy/types": "^4.3.2",
|
||||
"tslib": "^2.6.2"
|
||||
},
|
||||
"engines": {
|
||||
@ -4125,15 +4125,15 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@smithy/config-resolver": {
|
||||
"version": "4.1.4",
|
||||
"resolved": "https://registry.npmjs.org/@smithy/config-resolver/-/config-resolver-4.1.4.tgz",
|
||||
"integrity": "sha512-prmU+rDddxHOH0oNcwemL+SwnzcG65sBF2yXRO7aeXIn/xTlq2pX7JLVbkBnVLowHLg4/OL4+jBmv9hVrVGS+w==",
|
||||
"version": "4.1.5",
|
||||
"resolved": "https://registry.npmjs.org/@smithy/config-resolver/-/config-resolver-4.1.5.tgz",
|
||||
"integrity": "sha512-viuHMxBAqydkB0AfWwHIdwf/PRH2z5KHGUzqyRtS/Wv+n3IHI993Sk76VCA7dD/+GzgGOmlJDITfPcJC1nIVIw==",
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"@smithy/node-config-provider": "^4.1.3",
|
||||
"@smithy/types": "^4.3.1",
|
||||
"@smithy/node-config-provider": "^4.1.4",
|
||||
"@smithy/types": "^4.3.2",
|
||||
"@smithy/util-config-provider": "^4.0.0",
|
||||
"@smithy/util-middleware": "^4.0.4",
|
||||
"@smithy/util-middleware": "^4.0.5",
|
||||
"tslib": "^2.6.2"
|
||||
},
|
||||
"engines": {
|
||||
@ -4141,35 +4141,37 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@smithy/core": {
|
||||
"version": "3.7.2",
|
||||
"resolved": "https://registry.npmjs.org/@smithy/core/-/core-3.7.2.tgz",
|
||||
"integrity": "sha512-JoLw59sT5Bm8SAjFCYZyuCGxK8y3vovmoVbZWLDPTH5XpPEIwpFd9m90jjVMwoypDuB/SdVgje5Y4T7w50lJaw==",
|
||||
"version": "3.8.0",
|
||||
"resolved": "https://registry.npmjs.org/@smithy/core/-/core-3.8.0.tgz",
|
||||
"integrity": "sha512-EYqsIYJmkR1VhVE9pccnk353xhs+lB6btdutJEtsp7R055haMJp2yE16eSxw8fv+G0WUY6vqxyYOP8kOqawxYQ==",
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"@smithy/middleware-serde": "^4.0.8",
|
||||
"@smithy/protocol-http": "^5.1.2",
|
||||
"@smithy/types": "^4.3.1",
|
||||
"@smithy/middleware-serde": "^4.0.9",
|
||||
"@smithy/protocol-http": "^5.1.3",
|
||||
"@smithy/types": "^4.3.2",
|
||||
"@smithy/util-base64": "^4.0.0",
|
||||
"@smithy/util-body-length-browser": "^4.0.0",
|
||||
"@smithy/util-middleware": "^4.0.4",
|
||||
"@smithy/util-stream": "^4.2.3",
|
||||
"@smithy/util-middleware": "^4.0.5",
|
||||
"@smithy/util-stream": "^4.2.4",
|
||||
"@smithy/util-utf8": "^4.0.0",
|
||||
"tslib": "^2.6.2"
|
||||
"@types/uuid": "^9.0.1",
|
||||
"tslib": "^2.6.2",
|
||||
"uuid": "^9.0.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=18.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@smithy/credential-provider-imds": {
|
||||
"version": "4.0.6",
|
||||
"resolved": "https://registry.npmjs.org/@smithy/credential-provider-imds/-/credential-provider-imds-4.0.6.tgz",
|
||||
"integrity": "sha512-hKMWcANhUiNbCJouYkZ9V3+/Qf9pteR1dnwgdyzR09R4ODEYx8BbUysHwRSyex4rZ9zapddZhLFTnT4ZijR4pw==",
|
||||
"version": "4.0.7",
|
||||
"resolved": "https://registry.npmjs.org/@smithy/credential-provider-imds/-/credential-provider-imds-4.0.7.tgz",
|
||||
"integrity": "sha512-dDzrMXA8d8riFNiPvytxn0mNwR4B3h8lgrQ5UjAGu6T9z/kRg/Xncf4tEQHE/+t25sY8IH3CowcmWi+1U5B1Gw==",
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"@smithy/node-config-provider": "^4.1.3",
|
||||
"@smithy/property-provider": "^4.0.4",
|
||||
"@smithy/types": "^4.3.1",
|
||||
"@smithy/url-parser": "^4.0.4",
|
||||
"@smithy/node-config-provider": "^4.1.4",
|
||||
"@smithy/property-provider": "^4.0.5",
|
||||
"@smithy/types": "^4.3.2",
|
||||
"@smithy/url-parser": "^4.0.5",
|
||||
"tslib": "^2.6.2"
|
||||
},
|
||||
"engines": {
|
||||
@ -4177,13 +4179,13 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@smithy/eventstream-codec": {
|
||||
"version": "4.0.4",
|
||||
"resolved": "https://registry.npmjs.org/@smithy/eventstream-codec/-/eventstream-codec-4.0.4.tgz",
|
||||
"integrity": "sha512-7XoWfZqWb/QoR/rAU4VSi0mWnO2vu9/ltS6JZ5ZSZv0eovLVfDfu0/AX4ub33RsJTOth3TiFWSHS5YdztvFnig==",
|
||||
"version": "4.0.5",
|
||||
"resolved": "https://registry.npmjs.org/@smithy/eventstream-codec/-/eventstream-codec-4.0.5.tgz",
|
||||
"integrity": "sha512-miEUN+nz2UTNoRYRhRqVTJCx7jMeILdAurStT2XoS+mhokkmz1xAPp95DFW9Gxt4iF2VBqpeF9HbTQ3kY1viOA==",
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"@aws-crypto/crc32": "5.2.0",
|
||||
"@smithy/types": "^4.3.1",
|
||||
"@smithy/types": "^4.3.2",
|
||||
"@smithy/util-hex-encoding": "^4.0.0",
|
||||
"tslib": "^2.6.2"
|
||||
},
|
||||
@ -4192,13 +4194,13 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@smithy/eventstream-serde-browser": {
|
||||
"version": "4.0.4",
|
||||
"resolved": "https://registry.npmjs.org/@smithy/eventstream-serde-browser/-/eventstream-serde-browser-4.0.4.tgz",
|
||||
"integrity": "sha512-3fb/9SYaYqbpy/z/H3yIi0bYKyAa89y6xPmIqwr2vQiUT2St+avRt8UKwsWt9fEdEasc5d/V+QjrviRaX1JRFA==",
|
||||
"version": "4.0.5",
|
||||
"resolved": "https://registry.npmjs.org/@smithy/eventstream-serde-browser/-/eventstream-serde-browser-4.0.5.tgz",
|
||||
"integrity": "sha512-LCUQUVTbM6HFKzImYlSB9w4xafZmpdmZsOh9rIl7riPC3osCgGFVP+wwvYVw6pXda9PPT9TcEZxaq3XE81EdJQ==",
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"@smithy/eventstream-serde-universal": "^4.0.4",
|
||||
"@smithy/types": "^4.3.1",
|
||||
"@smithy/eventstream-serde-universal": "^4.0.5",
|
||||
"@smithy/types": "^4.3.2",
|
||||
"tslib": "^2.6.2"
|
||||
},
|
||||
"engines": {
|
||||
@ -4206,12 +4208,12 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@smithy/eventstream-serde-config-resolver": {
|
||||
"version": "4.1.2",
|
||||
"resolved": "https://registry.npmjs.org/@smithy/eventstream-serde-config-resolver/-/eventstream-serde-config-resolver-4.1.2.tgz",
|
||||
"integrity": "sha512-JGtambizrWP50xHgbzZI04IWU7LdI0nh/wGbqH3sJesYToMi2j/DcoElqyOcqEIG/D4tNyxgRuaqBXWE3zOFhQ==",
|
||||
"version": "4.1.3",
|
||||
"resolved": "https://registry.npmjs.org/@smithy/eventstream-serde-config-resolver/-/eventstream-serde-config-resolver-4.1.3.tgz",
|
||||
"integrity": "sha512-yTTzw2jZjn/MbHu1pURbHdpjGbCuMHWncNBpJnQAPxOVnFUAbSIUSwafiphVDjNV93TdBJWmeVAds7yl5QCkcA==",
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"@smithy/types": "^4.3.1",
|
||||
"@smithy/types": "^4.3.2",
|
||||
"tslib": "^2.6.2"
|
||||
},
|
||||
"engines": {
|
||||
@ -4219,13 +4221,13 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@smithy/eventstream-serde-node": {
|
||||
"version": "4.0.4",
|
||||
"resolved": "https://registry.npmjs.org/@smithy/eventstream-serde-node/-/eventstream-serde-node-4.0.4.tgz",
|
||||
"integrity": "sha512-RD6UwNZ5zISpOWPuhVgRz60GkSIp0dy1fuZmj4RYmqLVRtejFqQ16WmfYDdoSoAjlp1LX+FnZo+/hkdmyyGZ1w==",
|
||||
"version": "4.0.5",
|
||||
"resolved": "https://registry.npmjs.org/@smithy/eventstream-serde-node/-/eventstream-serde-node-4.0.5.tgz",
|
||||
"integrity": "sha512-lGS10urI4CNzz6YlTe5EYG0YOpsSp3ra8MXyco4aqSkQDuyZPIw2hcaxDU82OUVtK7UY9hrSvgWtpsW5D4rb4g==",
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"@smithy/eventstream-serde-universal": "^4.0.4",
|
||||
"@smithy/types": "^4.3.1",
|
||||
"@smithy/eventstream-serde-universal": "^4.0.5",
|
||||
"@smithy/types": "^4.3.2",
|
||||
"tslib": "^2.6.2"
|
||||
},
|
||||
"engines": {
|
||||
@ -4233,13 +4235,13 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@smithy/eventstream-serde-universal": {
|
||||
"version": "4.0.4",
|
||||
"resolved": "https://registry.npmjs.org/@smithy/eventstream-serde-universal/-/eventstream-serde-universal-4.0.4.tgz",
|
||||
"integrity": "sha512-UeJpOmLGhq1SLox79QWw/0n2PFX+oPRE1ZyRMxPIaFEfCqWaqpB7BU9C8kpPOGEhLF7AwEqfFbtwNxGy4ReENA==",
|
||||
"version": "4.0.5",
|
||||
"resolved": "https://registry.npmjs.org/@smithy/eventstream-serde-universal/-/eventstream-serde-universal-4.0.5.tgz",
|
||||
"integrity": "sha512-JFnmu4SU36YYw3DIBVao3FsJh4Uw65vVDIqlWT4LzR6gXA0F3KP0IXFKKJrhaVzCBhAuMsrUUaT5I+/4ZhF7aw==",
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"@smithy/eventstream-codec": "^4.0.4",
|
||||
"@smithy/types": "^4.3.1",
|
||||
"@smithy/eventstream-codec": "^4.0.5",
|
||||
"@smithy/types": "^4.3.2",
|
||||
"tslib": "^2.6.2"
|
||||
},
|
||||
"engines": {
|
||||
@ -4247,14 +4249,14 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@smithy/fetch-http-handler": {
|
||||
"version": "5.1.0",
|
||||
"resolved": "https://registry.npmjs.org/@smithy/fetch-http-handler/-/fetch-http-handler-5.1.0.tgz",
|
||||
"integrity": "sha512-mADw7MS0bYe2OGKkHYMaqarOXuDwRbO6ArD91XhHcl2ynjGCFF+hvqf0LyQcYxkA1zaWjefSkU7Ne9mqgApSgQ==",
|
||||
"version": "5.1.1",
|
||||
"resolved": "https://registry.npmjs.org/@smithy/fetch-http-handler/-/fetch-http-handler-5.1.1.tgz",
|
||||
"integrity": "sha512-61WjM0PWmZJR+SnmzaKI7t7G0UkkNFboDpzIdzSoy7TByUzlxo18Qlh9s71qug4AY4hlH/CwXdubMtkcNEb/sQ==",
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"@smithy/protocol-http": "^5.1.2",
|
||||
"@smithy/querystring-builder": "^4.0.4",
|
||||
"@smithy/types": "^4.3.1",
|
||||
"@smithy/protocol-http": "^5.1.3",
|
||||
"@smithy/querystring-builder": "^4.0.5",
|
||||
"@smithy/types": "^4.3.2",
|
||||
"@smithy/util-base64": "^4.0.0",
|
||||
"tslib": "^2.6.2"
|
||||
},
|
||||
@ -4263,14 +4265,14 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@smithy/hash-blob-browser": {
|
||||
"version": "4.0.4",
|
||||
"resolved": "https://registry.npmjs.org/@smithy/hash-blob-browser/-/hash-blob-browser-4.0.4.tgz",
|
||||
"integrity": "sha512-WszRiACJiQV3QG6XMV44i5YWlkrlsM5Yxgz4jvsksuu7LDXA6wAtypfPajtNTadzpJy3KyJPoWehYpmZGKUFIQ==",
|
||||
"version": "4.0.5",
|
||||
"resolved": "https://registry.npmjs.org/@smithy/hash-blob-browser/-/hash-blob-browser-4.0.5.tgz",
|
||||
"integrity": "sha512-F7MmCd3FH/Q2edhcKd+qulWkwfChHbc9nhguBlVjSUE6hVHhec3q6uPQ+0u69S6ppvLtR3eStfCuEKMXBXhvvA==",
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"@smithy/chunked-blob-reader": "^5.0.0",
|
||||
"@smithy/chunked-blob-reader-native": "^4.0.0",
|
||||
"@smithy/types": "^4.3.1",
|
||||
"@smithy/types": "^4.3.2",
|
||||
"tslib": "^2.6.2"
|
||||
},
|
||||
"engines": {
|
||||
@ -4278,12 +4280,12 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@smithy/hash-node": {
|
||||
"version": "4.0.4",
|
||||
"resolved": "https://registry.npmjs.org/@smithy/hash-node/-/hash-node-4.0.4.tgz",
|
||||
"integrity": "sha512-qnbTPUhCVnCgBp4z4BUJUhOEkVwxiEi1cyFM+Zj6o+aY8OFGxUQleKWq8ltgp3dujuhXojIvJWdoqpm6dVO3lQ==",
|
||||
"version": "4.0.5",
|
||||
"resolved": "https://registry.npmjs.org/@smithy/hash-node/-/hash-node-4.0.5.tgz",
|
||||
"integrity": "sha512-cv1HHkKhpyRb6ahD8Vcfb2Hgz67vNIXEp2vnhzfxLFGRukLCNEA5QdsorbUEzXma1Rco0u3rx5VTqbM06GcZqQ==",
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"@smithy/types": "^4.3.1",
|
||||
"@smithy/types": "^4.3.2",
|
||||
"@smithy/util-buffer-from": "^4.0.0",
|
||||
"@smithy/util-utf8": "^4.0.0",
|
||||
"tslib": "^2.6.2"
|
||||
@ -4293,12 +4295,12 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@smithy/hash-stream-node": {
|
||||
"version": "4.0.4",
|
||||
"resolved": "https://registry.npmjs.org/@smithy/hash-stream-node/-/hash-stream-node-4.0.4.tgz",
|
||||
"integrity": "sha512-wHo0d8GXyVmpmMh/qOR0R7Y46/G1y6OR8U+bSTB4ppEzRxd1xVAQ9xOE9hOc0bSjhz0ujCPAbfNLkLrpa6cevg==",
|
||||
"version": "4.0.5",
|
||||
"resolved": "https://registry.npmjs.org/@smithy/hash-stream-node/-/hash-stream-node-4.0.5.tgz",
|
||||
"integrity": "sha512-IJuDS3+VfWB67UC0GU0uYBG/TA30w+PlOaSo0GPm9UHS88A6rCP6uZxNjNYiyRtOcjv7TXn/60cW8ox1yuZsLg==",
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"@smithy/types": "^4.3.1",
|
||||
"@smithy/types": "^4.3.2",
|
||||
"@smithy/util-utf8": "^4.0.0",
|
||||
"tslib": "^2.6.2"
|
||||
},
|
||||
@ -4307,12 +4309,12 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@smithy/invalid-dependency": {
|
||||
"version": "4.0.4",
|
||||
"resolved": "https://registry.npmjs.org/@smithy/invalid-dependency/-/invalid-dependency-4.0.4.tgz",
|
||||
"integrity": "sha512-bNYMi7WKTJHu0gn26wg8OscncTt1t2b8KcsZxvOv56XA6cyXtOAAAaNP7+m45xfppXfOatXF3Sb1MNsLUgVLTw==",
|
||||
"version": "4.0.5",
|
||||
"resolved": "https://registry.npmjs.org/@smithy/invalid-dependency/-/invalid-dependency-4.0.5.tgz",
|
||||
"integrity": "sha512-IVnb78Qtf7EJpoEVo7qJ8BEXQwgC4n3igeJNNKEj/MLYtapnx8A67Zt/J3RXAj2xSO1910zk0LdFiygSemuLow==",
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"@smithy/types": "^4.3.1",
|
||||
"@smithy/types": "^4.3.2",
|
||||
"tslib": "^2.6.2"
|
||||
},
|
||||
"engines": {
|
||||
@ -4332,12 +4334,12 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@smithy/md5-js": {
|
||||
"version": "4.0.4",
|
||||
"resolved": "https://registry.npmjs.org/@smithy/md5-js/-/md5-js-4.0.4.tgz",
|
||||
"integrity": "sha512-uGLBVqcOwrLvGh/v/jw423yWHq/ofUGK1W31M2TNspLQbUV1Va0F5kTxtirkoHawODAZcjXTSGi7JwbnPcDPJg==",
|
||||
"version": "4.0.5",
|
||||
"resolved": "https://registry.npmjs.org/@smithy/md5-js/-/md5-js-4.0.5.tgz",
|
||||
"integrity": "sha512-8n2XCwdUbGr8W/XhMTaxILkVlw2QebkVTn5tm3HOcbPbOpWg89zr6dPXsH8xbeTsbTXlJvlJNTQsKAIoqQGbdA==",
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"@smithy/types": "^4.3.1",
|
||||
"@smithy/types": "^4.3.2",
|
||||
"@smithy/util-utf8": "^4.0.0",
|
||||
"tslib": "^2.6.2"
|
||||
},
|
||||
@ -4346,13 +4348,13 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@smithy/middleware-content-length": {
|
||||
"version": "4.0.4",
|
||||
"resolved": "https://registry.npmjs.org/@smithy/middleware-content-length/-/middleware-content-length-4.0.4.tgz",
|
||||
"integrity": "sha512-F7gDyfI2BB1Kc+4M6rpuOLne5LOcEknH1n6UQB69qv+HucXBR1rkzXBnQTB2q46sFy1PM/zuSJOB532yc8bg3w==",
|
||||
"version": "4.0.5",
|
||||
"resolved": "https://registry.npmjs.org/@smithy/middleware-content-length/-/middleware-content-length-4.0.5.tgz",
|
||||
"integrity": "sha512-l1jlNZoYzoCC7p0zCtBDE5OBXZ95yMKlRlftooE5jPWQn4YBPLgsp+oeHp7iMHaTGoUdFqmHOPa8c9G3gBsRpQ==",
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"@smithy/protocol-http": "^5.1.2",
|
||||
"@smithy/types": "^4.3.1",
|
||||
"@smithy/protocol-http": "^5.1.3",
|
||||
"@smithy/types": "^4.3.2",
|
||||
"tslib": "^2.6.2"
|
||||
},
|
||||
"engines": {
|
||||
@ -4360,18 +4362,18 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@smithy/middleware-endpoint": {
|
||||
"version": "4.1.17",
|
||||
"resolved": "https://registry.npmjs.org/@smithy/middleware-endpoint/-/middleware-endpoint-4.1.17.tgz",
|
||||
"integrity": "sha512-S3hSGLKmHG1m35p/MObQCBCdRsrpbPU8B129BVzRqRfDvQqPMQ14iO4LyRw+7LNizYc605COYAcjqgawqi+6jA==",
|
||||
"version": "4.1.18",
|
||||
"resolved": "https://registry.npmjs.org/@smithy/middleware-endpoint/-/middleware-endpoint-4.1.18.tgz",
|
||||
"integrity": "sha512-ZhvqcVRPZxnZlokcPaTwb+r+h4yOIOCJmx0v2d1bpVlmP465g3qpVSf7wxcq5zZdu4jb0H4yIMxuPwDJSQc3MQ==",
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"@smithy/core": "^3.7.2",
|
||||
"@smithy/middleware-serde": "^4.0.8",
|
||||
"@smithy/node-config-provider": "^4.1.3",
|
||||
"@smithy/shared-ini-file-loader": "^4.0.4",
|
||||
"@smithy/types": "^4.3.1",
|
||||
"@smithy/url-parser": "^4.0.4",
|
||||
"@smithy/util-middleware": "^4.0.4",
|
||||
"@smithy/core": "^3.8.0",
|
||||
"@smithy/middleware-serde": "^4.0.9",
|
||||
"@smithy/node-config-provider": "^4.1.4",
|
||||
"@smithy/shared-ini-file-loader": "^4.0.5",
|
||||
"@smithy/types": "^4.3.2",
|
||||
"@smithy/url-parser": "^4.0.5",
|
||||
"@smithy/util-middleware": "^4.0.5",
|
||||
"tslib": "^2.6.2"
|
||||
},
|
||||
"engines": {
|
||||
@ -4379,18 +4381,19 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@smithy/middleware-retry": {
|
||||
"version": "4.1.18",
|
||||
"resolved": "https://registry.npmjs.org/@smithy/middleware-retry/-/middleware-retry-4.1.18.tgz",
|
||||
"integrity": "sha512-bYLZ4DkoxSsPxpdmeapvAKy7rM5+25gR7PGxq2iMiecmbrRGBHj9s75N74Ylg+aBiw9i5jIowC/cLU2NR0qH8w==",
|
||||
"version": "4.1.19",
|
||||
"resolved": "https://registry.npmjs.org/@smithy/middleware-retry/-/middleware-retry-4.1.19.tgz",
|
||||
"integrity": "sha512-X58zx/NVECjeuUB6A8HBu4bhx72EoUz+T5jTMIyeNKx2lf+Gs9TmWPNNkH+5QF0COjpInP/xSpJGJ7xEnAklQQ==",
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"@smithy/node-config-provider": "^4.1.3",
|
||||
"@smithy/protocol-http": "^5.1.2",
|
||||
"@smithy/service-error-classification": "^4.0.6",
|
||||
"@smithy/smithy-client": "^4.4.9",
|
||||
"@smithy/types": "^4.3.1",
|
||||
"@smithy/util-middleware": "^4.0.4",
|
||||
"@smithy/util-retry": "^4.0.6",
|
||||
"@smithy/node-config-provider": "^4.1.4",
|
||||
"@smithy/protocol-http": "^5.1.3",
|
||||
"@smithy/service-error-classification": "^4.0.7",
|
||||
"@smithy/smithy-client": "^4.4.10",
|
||||
"@smithy/types": "^4.3.2",
|
||||
"@smithy/util-middleware": "^4.0.5",
|
||||
"@smithy/util-retry": "^4.0.7",
|
||||
"@types/uuid": "^9.0.1",
|
||||
"tslib": "^2.6.2",
|
||||
"uuid": "^9.0.1"
|
||||
},
|
||||
@ -4399,13 +4402,13 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@smithy/middleware-serde": {
|
||||
"version": "4.0.8",
|
||||
"resolved": "https://registry.npmjs.org/@smithy/middleware-serde/-/middleware-serde-4.0.8.tgz",
|
||||
"integrity": "sha512-iSSl7HJoJaGyMIoNn2B7czghOVwJ9nD7TMvLhMWeSB5vt0TnEYyRRqPJu/TqW76WScaNvYYB8nRoiBHR9S1Ddw==",
|
||||
"version": "4.0.9",
|
||||
"resolved": "https://registry.npmjs.org/@smithy/middleware-serde/-/middleware-serde-4.0.9.tgz",
|
||||
"integrity": "sha512-uAFFR4dpeoJPGz8x9mhxp+RPjo5wW0QEEIPPPbLXiRRWeCATf/Km3gKIVR5vaP8bN1kgsPhcEeh+IZvUlBv6Xg==",
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"@smithy/protocol-http": "^5.1.2",
|
||||
"@smithy/types": "^4.3.1",
|
||||
"@smithy/protocol-http": "^5.1.3",
|
||||
"@smithy/types": "^4.3.2",
|
||||
"tslib": "^2.6.2"
|
||||
},
|
||||
"engines": {
|
||||
@ -4413,12 +4416,12 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@smithy/middleware-stack": {
|
||||
"version": "4.0.4",
|
||||
"resolved": "https://registry.npmjs.org/@smithy/middleware-stack/-/middleware-stack-4.0.4.tgz",
|
||||
"integrity": "sha512-kagK5ggDrBUCCzI93ft6DjteNSfY8Ulr83UtySog/h09lTIOAJ/xUSObutanlPT0nhoHAkpmW9V5K8oPyLh+QA==",
|
||||
"version": "4.0.5",
|
||||
"resolved": "https://registry.npmjs.org/@smithy/middleware-stack/-/middleware-stack-4.0.5.tgz",
|
||||
"integrity": "sha512-/yoHDXZPh3ocRVyeWQFvC44u8seu3eYzZRveCMfgMOBcNKnAmOvjbL9+Cp5XKSIi9iYA9PECUuW2teDAk8T+OQ==",
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"@smithy/types": "^4.3.1",
|
||||
"@smithy/types": "^4.3.2",
|
||||
"tslib": "^2.6.2"
|
||||
},
|
||||
"engines": {
|
||||
@ -4426,14 +4429,14 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@smithy/node-config-provider": {
|
||||
"version": "4.1.3",
|
||||
"resolved": "https://registry.npmjs.org/@smithy/node-config-provider/-/node-config-provider-4.1.3.tgz",
|
||||
"integrity": "sha512-HGHQr2s59qaU1lrVH6MbLlmOBxadtzTsoO4c+bF5asdgVik3I8o7JIOzoeqWc5MjVa+vD36/LWE0iXKpNqooRw==",
|
||||
"version": "4.1.4",
|
||||
"resolved": "https://registry.npmjs.org/@smithy/node-config-provider/-/node-config-provider-4.1.4.tgz",
|
||||
"integrity": "sha512-+UDQV/k42jLEPPHSn39l0Bmc4sB1xtdI9Gd47fzo/0PbXzJ7ylgaOByVjF5EeQIumkepnrJyfx86dPa9p47Y+w==",
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"@smithy/property-provider": "^4.0.4",
|
||||
"@smithy/shared-ini-file-loader": "^4.0.4",
|
||||
"@smithy/types": "^4.3.1",
|
||||
"@smithy/property-provider": "^4.0.5",
|
||||
"@smithy/shared-ini-file-loader": "^4.0.5",
|
||||
"@smithy/types": "^4.3.2",
|
||||
"tslib": "^2.6.2"
|
||||
},
|
||||
"engines": {
|
||||
@ -4441,15 +4444,15 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@smithy/node-http-handler": {
|
||||
"version": "4.1.0",
|
||||
"resolved": "https://registry.npmjs.org/@smithy/node-http-handler/-/node-http-handler-4.1.0.tgz",
|
||||
"integrity": "sha512-vqfSiHz2v8b3TTTrdXi03vNz1KLYYS3bhHCDv36FYDqxT7jvTll1mMnCrkD+gOvgwybuunh/2VmvOMqwBegxEg==",
|
||||
"version": "4.1.1",
|
||||
"resolved": "https://registry.npmjs.org/@smithy/node-http-handler/-/node-http-handler-4.1.1.tgz",
|
||||
"integrity": "sha512-RHnlHqFpoVdjSPPiYy/t40Zovf3BBHc2oemgD7VsVTFFZrU5erFFe0n52OANZZ/5sbshgD93sOh5r6I35Xmpaw==",
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"@smithy/abort-controller": "^4.0.4",
|
||||
"@smithy/protocol-http": "^5.1.2",
|
||||
"@smithy/querystring-builder": "^4.0.4",
|
||||
"@smithy/types": "^4.3.1",
|
||||
"@smithy/abort-controller": "^4.0.5",
|
||||
"@smithy/protocol-http": "^5.1.3",
|
||||
"@smithy/querystring-builder": "^4.0.5",
|
||||
"@smithy/types": "^4.3.2",
|
||||
"tslib": "^2.6.2"
|
||||
},
|
||||
"engines": {
|
||||
@ -4457,12 +4460,12 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@smithy/property-provider": {
|
||||
"version": "4.0.4",
|
||||
"resolved": "https://registry.npmjs.org/@smithy/property-provider/-/property-provider-4.0.4.tgz",
|
||||
"integrity": "sha512-qHJ2sSgu4FqF4U/5UUp4DhXNmdTrgmoAai6oQiM+c5RZ/sbDwJ12qxB1M6FnP+Tn/ggkPZf9ccn4jqKSINaquw==",
|
||||
"version": "4.0.5",
|
||||
"resolved": "https://registry.npmjs.org/@smithy/property-provider/-/property-provider-4.0.5.tgz",
|
||||
"integrity": "sha512-R/bswf59T/n9ZgfgUICAZoWYKBHcsVDurAGX88zsiUtOTA/xUAPyiT+qkNCPwFn43pZqN84M4MiUsbSGQmgFIQ==",
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"@smithy/types": "^4.3.1",
|
||||
"@smithy/types": "^4.3.2",
|
||||
"tslib": "^2.6.2"
|
||||
},
|
||||
"engines": {
|
||||
@ -4470,12 +4473,12 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@smithy/protocol-http": {
|
||||
"version": "5.1.2",
|
||||
"resolved": "https://registry.npmjs.org/@smithy/protocol-http/-/protocol-http-5.1.2.tgz",
|
||||
"integrity": "sha512-rOG5cNLBXovxIrICSBm95dLqzfvxjEmuZx4KK3hWwPFHGdW3lxY0fZNXfv2zebfRO7sJZ5pKJYHScsqopeIWtQ==",
|
||||
"version": "5.1.3",
|
||||
"resolved": "https://registry.npmjs.org/@smithy/protocol-http/-/protocol-http-5.1.3.tgz",
|
||||
"integrity": "sha512-fCJd2ZR7D22XhDY0l+92pUag/7je2BztPRQ01gU5bMChcyI0rlly7QFibnYHzcxDvccMjlpM/Q1ev8ceRIb48w==",
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"@smithy/types": "^4.3.1",
|
||||
"@smithy/types": "^4.3.2",
|
||||
"tslib": "^2.6.2"
|
||||
},
|
||||
"engines": {
|
||||
@ -4483,12 +4486,12 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@smithy/querystring-builder": {
|
||||
"version": "4.0.4",
|
||||
"resolved": "https://registry.npmjs.org/@smithy/querystring-builder/-/querystring-builder-4.0.4.tgz",
|
||||
"integrity": "sha512-SwREZcDnEYoh9tLNgMbpop+UTGq44Hl9tdj3rf+yeLcfH7+J8OXEBaMc2kDxtyRHu8BhSg9ADEx0gFHvpJgU8w==",
|
||||
"version": "4.0.5",
|
||||
"resolved": "https://registry.npmjs.org/@smithy/querystring-builder/-/querystring-builder-4.0.5.tgz",
|
||||
"integrity": "sha512-NJeSCU57piZ56c+/wY+AbAw6rxCCAOZLCIniRE7wqvndqxcKKDOXzwWjrY7wGKEISfhL9gBbAaWWgHsUGedk+A==",
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"@smithy/types": "^4.3.1",
|
||||
"@smithy/types": "^4.3.2",
|
||||
"@smithy/util-uri-escape": "^4.0.0",
|
||||
"tslib": "^2.6.2"
|
||||
},
|
||||
@ -4497,12 +4500,12 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@smithy/querystring-parser": {
|
||||
"version": "4.0.4",
|
||||
"resolved": "https://registry.npmjs.org/@smithy/querystring-parser/-/querystring-parser-4.0.4.tgz",
|
||||
"integrity": "sha512-6yZf53i/qB8gRHH/l2ZwUG5xgkPgQF15/KxH0DdXMDHjesA9MeZje/853ifkSY0x4m5S+dfDZ+c4x439PF0M2w==",
|
||||
"version": "4.0.5",
|
||||
"resolved": "https://registry.npmjs.org/@smithy/querystring-parser/-/querystring-parser-4.0.5.tgz",
|
||||
"integrity": "sha512-6SV7md2CzNG/WUeTjVe6Dj8noH32r4MnUeFKZrnVYsQxpGSIcphAanQMayi8jJLZAWm6pdM9ZXvKCpWOsIGg0w==",
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"@smithy/types": "^4.3.1",
|
||||
"@smithy/types": "^4.3.2",
|
||||
"tslib": "^2.6.2"
|
||||
},
|
||||
"engines": {
|
||||
@ -4510,24 +4513,24 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@smithy/service-error-classification": {
|
||||
"version": "4.0.6",
|
||||
"resolved": "https://registry.npmjs.org/@smithy/service-error-classification/-/service-error-classification-4.0.6.tgz",
|
||||
"integrity": "sha512-RRoTDL//7xi4tn5FrN2NzH17jbgmnKidUqd4KvquT0954/i6CXXkh1884jBiunq24g9cGtPBEXlU40W6EpNOOg==",
|
||||
"version": "4.0.7",
|
||||
"resolved": "https://registry.npmjs.org/@smithy/service-error-classification/-/service-error-classification-4.0.7.tgz",
|
||||
"integrity": "sha512-XvRHOipqpwNhEjDf2L5gJowZEm5nsxC16pAZOeEcsygdjv9A2jdOh3YoDQvOXBGTsaJk6mNWtzWalOB9976Wlg==",
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"@smithy/types": "^4.3.1"
|
||||
"@smithy/types": "^4.3.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=18.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@smithy/shared-ini-file-loader": {
|
||||
"version": "4.0.4",
|
||||
"resolved": "https://registry.npmjs.org/@smithy/shared-ini-file-loader/-/shared-ini-file-loader-4.0.4.tgz",
|
||||
"integrity": "sha512-63X0260LoFBjrHifPDs+nM9tV0VMkOTl4JRMYNuKh/f5PauSjowTfvF3LogfkWdcPoxsA9UjqEOgjeYIbhb7Nw==",
|
||||
"version": "4.0.5",
|
||||
"resolved": "https://registry.npmjs.org/@smithy/shared-ini-file-loader/-/shared-ini-file-loader-4.0.5.tgz",
|
||||
"integrity": "sha512-YVVwehRDuehgoXdEL4r1tAAzdaDgaC9EQvhK0lEbfnbrd0bd5+CTQumbdPryX3J2shT7ZqQE+jPW4lmNBAB8JQ==",
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"@smithy/types": "^4.3.1",
|
||||
"@smithy/types": "^4.3.2",
|
||||
"tslib": "^2.6.2"
|
||||
},
|
||||
"engines": {
|
||||
@ -4535,16 +4538,16 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@smithy/signature-v4": {
|
||||
"version": "5.1.2",
|
||||
"resolved": "https://registry.npmjs.org/@smithy/signature-v4/-/signature-v4-5.1.2.tgz",
|
||||
"integrity": "sha512-d3+U/VpX7a60seHziWnVZOHuEgJlclufjkS6zhXvxcJgkJq4UWdH5eOBLzHRMx6gXjsdT9h6lfpmLzbrdupHgQ==",
|
||||
"version": "5.1.3",
|
||||
"resolved": "https://registry.npmjs.org/@smithy/signature-v4/-/signature-v4-5.1.3.tgz",
|
||||
"integrity": "sha512-mARDSXSEgllNzMw6N+mC+r1AQlEBO3meEAkR/UlfAgnMzJUB3goRBWgip1EAMG99wh36MDqzo86SfIX5Y+VEaw==",
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"@smithy/is-array-buffer": "^4.0.0",
|
||||
"@smithy/protocol-http": "^5.1.2",
|
||||
"@smithy/types": "^4.3.1",
|
||||
"@smithy/protocol-http": "^5.1.3",
|
||||
"@smithy/types": "^4.3.2",
|
||||
"@smithy/util-hex-encoding": "^4.0.0",
|
||||
"@smithy/util-middleware": "^4.0.4",
|
||||
"@smithy/util-middleware": "^4.0.5",
|
||||
"@smithy/util-uri-escape": "^4.0.0",
|
||||
"@smithy/util-utf8": "^4.0.0",
|
||||
"tslib": "^2.6.2"
|
||||
@ -4554,17 +4557,17 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@smithy/smithy-client": {
|
||||
"version": "4.4.9",
|
||||
"resolved": "https://registry.npmjs.org/@smithy/smithy-client/-/smithy-client-4.4.9.tgz",
|
||||
"integrity": "sha512-mbMg8mIUAWwMmb74LoYiArP04zWElPzDoA1jVOp3or0cjlDMgoS6WTC3QXK0Vxoc9I4zdrX0tq6qsOmaIoTWEQ==",
|
||||
"version": "4.4.10",
|
||||
"resolved": "https://registry.npmjs.org/@smithy/smithy-client/-/smithy-client-4.4.10.tgz",
|
||||
"integrity": "sha512-iW6HjXqN0oPtRS0NK/zzZ4zZeGESIFcxj2FkWed3mcK8jdSdHzvnCKXSjvewESKAgGKAbJRA+OsaqKhkdYRbQQ==",
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"@smithy/core": "^3.7.2",
|
||||
"@smithy/middleware-endpoint": "^4.1.17",
|
||||
"@smithy/middleware-stack": "^4.0.4",
|
||||
"@smithy/protocol-http": "^5.1.2",
|
||||
"@smithy/types": "^4.3.1",
|
||||
"@smithy/util-stream": "^4.2.3",
|
||||
"@smithy/core": "^3.8.0",
|
||||
"@smithy/middleware-endpoint": "^4.1.18",
|
||||
"@smithy/middleware-stack": "^4.0.5",
|
||||
"@smithy/protocol-http": "^5.1.3",
|
||||
"@smithy/types": "^4.3.2",
|
||||
"@smithy/util-stream": "^4.2.4",
|
||||
"tslib": "^2.6.2"
|
||||
},
|
||||
"engines": {
|
||||
@ -4572,9 +4575,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@smithy/types": {
|
||||
"version": "4.3.1",
|
||||
"resolved": "https://registry.npmjs.org/@smithy/types/-/types-4.3.1.tgz",
|
||||
"integrity": "sha512-UqKOQBL2x6+HWl3P+3QqFD4ncKq0I8Nuz9QItGv5WuKuMHuuwlhvqcZCoXGfc+P1QmfJE7VieykoYYmrOoFJxA==",
|
||||
"version": "4.3.2",
|
||||
"resolved": "https://registry.npmjs.org/@smithy/types/-/types-4.3.2.tgz",
|
||||
"integrity": "sha512-QO4zghLxiQ5W9UZmX2Lo0nta2PuE1sSrXUYDoaB6HMR762C0P7v/HEPHf6ZdglTVssJG1bsrSBxdc3quvDSihw==",
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"tslib": "^2.6.2"
|
||||
@ -4584,13 +4587,13 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@smithy/url-parser": {
|
||||
"version": "4.0.4",
|
||||
"resolved": "https://registry.npmjs.org/@smithy/url-parser/-/url-parser-4.0.4.tgz",
|
||||
"integrity": "sha512-eMkc144MuN7B0TDA4U2fKs+BqczVbk3W+qIvcoCY6D1JY3hnAdCuhCZODC+GAeaxj0p6Jroz4+XMUn3PCxQQeQ==",
|
||||
"version": "4.0.5",
|
||||
"resolved": "https://registry.npmjs.org/@smithy/url-parser/-/url-parser-4.0.5.tgz",
|
||||
"integrity": "sha512-j+733Um7f1/DXjYhCbvNXABV53NyCRRA54C7bNEIxNPs0YjfRxeMKjjgm2jvTYrciZyCjsicHwQ6Q0ylo+NAUw==",
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"@smithy/querystring-parser": "^4.0.4",
|
||||
"@smithy/types": "^4.3.1",
|
||||
"@smithy/querystring-parser": "^4.0.5",
|
||||
"@smithy/types": "^4.3.2",
|
||||
"tslib": "^2.6.2"
|
||||
},
|
||||
"engines": {
|
||||
@ -4661,14 +4664,14 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@smithy/util-defaults-mode-browser": {
|
||||
"version": "4.0.25",
|
||||
"resolved": "https://registry.npmjs.org/@smithy/util-defaults-mode-browser/-/util-defaults-mode-browser-4.0.25.tgz",
|
||||
"integrity": "sha512-pxEWsxIsOPLfKNXvpgFHBGFC3pKYKUFhrud1kyooO9CJai6aaKDHfT10Mi5iiipPXN/JhKAu3qX9o75+X85OdQ==",
|
||||
"version": "4.0.26",
|
||||
"resolved": "https://registry.npmjs.org/@smithy/util-defaults-mode-browser/-/util-defaults-mode-browser-4.0.26.tgz",
|
||||
"integrity": "sha512-xgl75aHIS/3rrGp7iTxQAOELYeyiwBu+eEgAk4xfKwJJ0L8VUjhO2shsDpeil54BOFsqmk5xfdesiewbUY5tKQ==",
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"@smithy/property-provider": "^4.0.4",
|
||||
"@smithy/smithy-client": "^4.4.9",
|
||||
"@smithy/types": "^4.3.1",
|
||||
"@smithy/property-provider": "^4.0.5",
|
||||
"@smithy/smithy-client": "^4.4.10",
|
||||
"@smithy/types": "^4.3.2",
|
||||
"bowser": "^2.11.0",
|
||||
"tslib": "^2.6.2"
|
||||
},
|
||||
@ -4677,17 +4680,17 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@smithy/util-defaults-mode-node": {
|
||||
"version": "4.0.25",
|
||||
"resolved": "https://registry.npmjs.org/@smithy/util-defaults-mode-node/-/util-defaults-mode-node-4.0.25.tgz",
|
||||
"integrity": "sha512-+w4n4hKFayeCyELZLfsSQG5mCC3TwSkmRHv4+el5CzFU8ToQpYGhpV7mrRzqlwKkntlPilT1HJy1TVeEvEjWOQ==",
|
||||
"version": "4.0.26",
|
||||
"resolved": "https://registry.npmjs.org/@smithy/util-defaults-mode-node/-/util-defaults-mode-node-4.0.26.tgz",
|
||||
"integrity": "sha512-z81yyIkGiLLYVDetKTUeCZQ8x20EEzvQjrqJtb/mXnevLq2+w3XCEWTJ2pMp401b6BkEkHVfXb/cROBpVauLMQ==",
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"@smithy/config-resolver": "^4.1.4",
|
||||
"@smithy/credential-provider-imds": "^4.0.6",
|
||||
"@smithy/node-config-provider": "^4.1.3",
|
||||
"@smithy/property-provider": "^4.0.4",
|
||||
"@smithy/smithy-client": "^4.4.9",
|
||||
"@smithy/types": "^4.3.1",
|
||||
"@smithy/config-resolver": "^4.1.5",
|
||||
"@smithy/credential-provider-imds": "^4.0.7",
|
||||
"@smithy/node-config-provider": "^4.1.4",
|
||||
"@smithy/property-provider": "^4.0.5",
|
||||
"@smithy/smithy-client": "^4.4.10",
|
||||
"@smithy/types": "^4.3.2",
|
||||
"tslib": "^2.6.2"
|
||||
},
|
||||
"engines": {
|
||||
@ -4695,13 +4698,13 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@smithy/util-endpoints": {
|
||||
"version": "3.0.6",
|
||||
"resolved": "https://registry.npmjs.org/@smithy/util-endpoints/-/util-endpoints-3.0.6.tgz",
|
||||
"integrity": "sha512-YARl3tFL3WgPuLzljRUnrS2ngLiUtkwhQtj8PAL13XZSyUiNLQxwG3fBBq3QXFqGFUXepIN73pINp3y8c2nBmA==",
|
||||
"version": "3.0.7",
|
||||
"resolved": "https://registry.npmjs.org/@smithy/util-endpoints/-/util-endpoints-3.0.7.tgz",
|
||||
"integrity": "sha512-klGBP+RpBp6V5JbrY2C/VKnHXn3d5V2YrifZbmMY8os7M6m8wdYFoO6w/fe5VkP+YVwrEktW3IWYaSQVNZJ8oQ==",
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"@smithy/node-config-provider": "^4.1.3",
|
||||
"@smithy/types": "^4.3.1",
|
||||
"@smithy/node-config-provider": "^4.1.4",
|
||||
"@smithy/types": "^4.3.2",
|
||||
"tslib": "^2.6.2"
|
||||
},
|
||||
"engines": {
|
||||
@ -4721,12 +4724,12 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@smithy/util-middleware": {
|
||||
"version": "4.0.4",
|
||||
"resolved": "https://registry.npmjs.org/@smithy/util-middleware/-/util-middleware-4.0.4.tgz",
|
||||
"integrity": "sha512-9MLKmkBmf4PRb0ONJikCbCwORACcil6gUWojwARCClT7RmLzF04hUR4WdRprIXal7XVyrddadYNfp2eF3nrvtQ==",
|
||||
"version": "4.0.5",
|
||||
"resolved": "https://registry.npmjs.org/@smithy/util-middleware/-/util-middleware-4.0.5.tgz",
|
||||
"integrity": "sha512-N40PfqsZHRSsByGB81HhSo+uvMxEHT+9e255S53pfBw/wI6WKDI7Jw9oyu5tJTLwZzV5DsMha3ji8jk9dsHmQQ==",
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"@smithy/types": "^4.3.1",
|
||||
"@smithy/types": "^4.3.2",
|
||||
"tslib": "^2.6.2"
|
||||
},
|
||||
"engines": {
|
||||
@ -4734,13 +4737,13 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@smithy/util-retry": {
|
||||
"version": "4.0.6",
|
||||
"resolved": "https://registry.npmjs.org/@smithy/util-retry/-/util-retry-4.0.6.tgz",
|
||||
"integrity": "sha512-+YekoF2CaSMv6zKrA6iI/N9yva3Gzn4L6n35Luydweu5MMPYpiGZlWqehPHDHyNbnyaYlz/WJyYAZnC+loBDZg==",
|
||||
"version": "4.0.7",
|
||||
"resolved": "https://registry.npmjs.org/@smithy/util-retry/-/util-retry-4.0.7.tgz",
|
||||
"integrity": "sha512-TTO6rt0ppK70alZpkjwy+3nQlTiqNfoXja+qwuAchIEAIoSZW8Qyd76dvBv3I5bCpE38APafG23Y/u270NspiQ==",
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"@smithy/service-error-classification": "^4.0.6",
|
||||
"@smithy/types": "^4.3.1",
|
||||
"@smithy/service-error-classification": "^4.0.7",
|
||||
"@smithy/types": "^4.3.2",
|
||||
"tslib": "^2.6.2"
|
||||
},
|
||||
"engines": {
|
||||
@ -4748,14 +4751,14 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@smithy/util-stream": {
|
||||
"version": "4.2.3",
|
||||
"resolved": "https://registry.npmjs.org/@smithy/util-stream/-/util-stream-4.2.3.tgz",
|
||||
"integrity": "sha512-cQn412DWHHFNKrQfbHY8vSFI3nTROY1aIKji9N0tpp8gUABRilr7wdf8fqBbSlXresobM+tQFNk6I+0LXK/YZg==",
|
||||
"version": "4.2.4",
|
||||
"resolved": "https://registry.npmjs.org/@smithy/util-stream/-/util-stream-4.2.4.tgz",
|
||||
"integrity": "sha512-vSKnvNZX2BXzl0U2RgCLOwWaAP9x/ddd/XobPK02pCbzRm5s55M53uwb1rl/Ts7RXZvdJZerPkA+en2FDghLuQ==",
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"@smithy/fetch-http-handler": "^5.1.0",
|
||||
"@smithy/node-http-handler": "^4.1.0",
|
||||
"@smithy/types": "^4.3.1",
|
||||
"@smithy/fetch-http-handler": "^5.1.1",
|
||||
"@smithy/node-http-handler": "^4.1.1",
|
||||
"@smithy/types": "^4.3.2",
|
||||
"@smithy/util-base64": "^4.0.0",
|
||||
"@smithy/util-buffer-from": "^4.0.0",
|
||||
"@smithy/util-hex-encoding": "^4.0.0",
|
||||
@ -4792,13 +4795,13 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@smithy/util-waiter": {
|
||||
"version": "4.0.6",
|
||||
"resolved": "https://registry.npmjs.org/@smithy/util-waiter/-/util-waiter-4.0.6.tgz",
|
||||
"integrity": "sha512-slcr1wdRbX7NFphXZOxtxRNA7hXAAtJAXJDE/wdoMAos27SIquVCKiSqfB6/28YzQ8FCsB5NKkhdM5gMADbqxg==",
|
||||
"version": "4.0.7",
|
||||
"resolved": "https://registry.npmjs.org/@smithy/util-waiter/-/util-waiter-4.0.7.tgz",
|
||||
"integrity": "sha512-mYqtQXPmrwvUljaHyGxYUIIRI3qjBTEb/f5QFi3A6VlxhpmZd5mWXn9W+qUkf2pVE1Hv3SqxefiZOPGdxmO64A==",
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"@smithy/abort-controller": "^4.0.4",
|
||||
"@smithy/types": "^4.3.1",
|
||||
"@smithy/abort-controller": "^4.0.5",
|
||||
"@smithy/types": "^4.3.2",
|
||||
"tslib": "^2.6.2"
|
||||
},
|
||||
"engines": {
|
||||
@ -7481,9 +7484,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/electron-to-chromium": {
|
||||
"version": "1.5.195",
|
||||
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.195.tgz",
|
||||
"integrity": "sha512-URclP0iIaDUzqcAyV1v2PgduJ9N0IdXmWsnPzPfelvBmjmZzEy6xJcjb1cXj+TbYqXgtLrjHEoaSIdTYhw4ezg==",
|
||||
"version": "1.5.197",
|
||||
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.197.tgz",
|
||||
"integrity": "sha512-m1xWB3g7vJ6asIFz+2pBUbq3uGmfmln1M9SSvBe4QIFWYrRHylP73zL/3nMjDmwz8V+1xAXQDfBd6+HPW0WvDQ==",
|
||||
"dev": true,
|
||||
"license": "ISC"
|
||||
},
|
||||
|
||||
@ -173,7 +173,7 @@ export const getRoomRolesAndPermissions = async (req: Request, res: Response) =>
|
||||
|
||||
logger.verbose(`Getting roles and associated permissions for room '${roomId}'`);
|
||||
const moderatorPermissions = participantService.getParticipantPermissions(roomId, ParticipantRole.MODERATOR);
|
||||
const publisherPermissions = participantService.getParticipantPermissions(roomId, ParticipantRole.PUBLISHER);
|
||||
const speakerPermissions = participantService.getParticipantPermissions(roomId, ParticipantRole.SPEAKER);
|
||||
|
||||
const rolesAndPermissions = [
|
||||
{
|
||||
@ -181,8 +181,8 @@ export const getRoomRolesAndPermissions = async (req: Request, res: Response) =>
|
||||
permissions: moderatorPermissions
|
||||
},
|
||||
{
|
||||
role: ParticipantRole.PUBLISHER,
|
||||
permissions: publisherPermissions
|
||||
role: ParticipantRole.SPEAKER,
|
||||
permissions: speakerPermissions
|
||||
}
|
||||
];
|
||||
res.status(200).json(rolesAndPermissions);
|
||||
|
||||
@ -22,24 +22,24 @@ export class MeetRoomHelper {
|
||||
}
|
||||
|
||||
/**
|
||||
* Extracts publisher and moderator secrets from a MeetRoom object's URLs.
|
||||
* Extracts speaker and moderator secrets from a MeetRoom object's URLs.
|
||||
*
|
||||
* This method parses the 'secret' query parameter from both publisher and moderator
|
||||
* This method parses the 'secret' query parameter from both speaker and moderator
|
||||
* room URLs associated with the meeting room.
|
||||
*
|
||||
* @param room - The MeetRoom object containing publisherRoomUrl and moderatorRoomUrl properties
|
||||
* @param room - The MeetRoom object containing speakerRoomUrl and moderatorRoomUrl properties
|
||||
* @returns An object containing the extracted secrets with the following properties:
|
||||
* - publisherSecret: The secret extracted from the publisher room URL
|
||||
* - speakerSecret: The secret extracted from the speaker room URL
|
||||
* - moderatorSecret: The secret extracted from the moderator room URL
|
||||
*/
|
||||
static extractSecretsFromRoom(room: MeetRoom): { publisherSecret: string; moderatorSecret: string } {
|
||||
const { publisherRoomUrl, moderatorRoomUrl } = room;
|
||||
static extractSecretsFromRoom(room: MeetRoom): { speakerSecret: string; moderatorSecret: string } {
|
||||
const { speakerRoomUrl, moderatorRoomUrl } = room;
|
||||
|
||||
const publisherUrl = new URL(publisherRoomUrl);
|
||||
const publisherSecret = publisherUrl.searchParams.get('secret') || '';
|
||||
const speakerUrl = new URL(speakerRoomUrl);
|
||||
const speakerSecret = speakerUrl.searchParams.get('secret') || '';
|
||||
const moderatorUrl = new URL(moderatorRoomUrl);
|
||||
const moderatorSecret = moderatorUrl.searchParams.get('secret') || '';
|
||||
return { publisherSecret, moderatorSecret };
|
||||
return { speakerSecret, moderatorSecret };
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@ -98,7 +98,7 @@ export const participantTokenValidator = async (req: Request) => {
|
||||
// Check if the participant role is provided in the request headers
|
||||
// This is required to distinguish roles when multiple are present in the token
|
||||
const participantRole = req.headers[INTERNAL_CONFIG.PARTICIPANT_ROLE_HEADER];
|
||||
const allRoles = [ParticipantRole.MODERATOR, ParticipantRole.PUBLISHER];
|
||||
const allRoles = [ParticipantRole.MODERATOR, ParticipantRole.SPEAKER];
|
||||
|
||||
if (!participantRole || !allRoles.includes(participantRole as ParticipantRole)) {
|
||||
throw errorWithControl(errorInvalidParticipantRole(), true);
|
||||
|
||||
@ -67,7 +67,7 @@ const validForceQueryParam = () =>
|
||||
const RecordingAccessSchema: z.ZodType<MeetRecordingAccess> = z.enum([
|
||||
MeetRecordingAccess.ADMIN,
|
||||
MeetRecordingAccess.ADMIN_MODERATOR,
|
||||
MeetRecordingAccess.ADMIN_MODERATOR_PUBLISHER
|
||||
MeetRecordingAccess.ADMIN_MODERATOR_SPEAKER
|
||||
]);
|
||||
|
||||
const RecordingPreferencesSchema: z.ZodType<MeetRecordingPreferences> = z
|
||||
@ -116,7 +116,7 @@ const RoomRequestOptionsSchema: z.ZodType<MeetRoomOptions> = z.object({
|
||||
)
|
||||
.optional(),
|
||||
preferences: RoomPreferencesSchema.optional().default({
|
||||
recordingPreferences: { enabled: true, allowAccessTo: MeetRecordingAccess.ADMIN_MODERATOR_PUBLISHER },
|
||||
recordingPreferences: { enabled: true, allowAccessTo: MeetRecordingAccess.ADMIN_MODERATOR_SPEAKER },
|
||||
chatPreferences: { enabled: true },
|
||||
virtualBackgroundPreferences: { enabled: true }
|
||||
})
|
||||
|
||||
@ -82,8 +82,8 @@ export class ParticipantService {
|
||||
switch (role) {
|
||||
case ParticipantRole.MODERATOR:
|
||||
return this.generateModeratorPermissions(roomId, addJoinPermission);
|
||||
case ParticipantRole.PUBLISHER:
|
||||
return this.generatePublisherPermissions(roomId, addJoinPermission);
|
||||
case ParticipantRole.SPEAKER:
|
||||
return this.generateSpeakerPermissions(roomId, addJoinPermission);
|
||||
default:
|
||||
throw new Error(`Role ${role} not supported`);
|
||||
}
|
||||
@ -107,7 +107,7 @@ export class ParticipantService {
|
||||
};
|
||||
}
|
||||
|
||||
protected generatePublisherPermissions(roomId: string, addJoinPermission = true): ParticipantPermissions {
|
||||
protected generateSpeakerPermissions(roomId: string, addJoinPermission = true): ParticipantPermissions {
|
||||
return {
|
||||
livekit: {
|
||||
roomJoin: addJoinPermission,
|
||||
|
||||
@ -81,7 +81,7 @@ export class RoomService {
|
||||
autoDeletionDate,
|
||||
preferences,
|
||||
moderatorRoomUrl: `${baseUrl}/room/${roomId}?secret=${secureUid(10)}`,
|
||||
publisherRoomUrl: `${baseUrl}/room/${roomId}?secret=${secureUid(10)}`
|
||||
speakerRoomUrl: `${baseUrl}/room/${roomId}?secret=${secureUid(10)}`
|
||||
};
|
||||
|
||||
await this.storageService.saveMeetRoom(meetRoom);
|
||||
@ -195,8 +195,8 @@ export class RoomService {
|
||||
|
||||
const filteredRoom = UtilsHelper.filterObjectFields(meetRoom, fields);
|
||||
|
||||
// Remove moderatorRoomUrl if the participant is a publisher to prevent access to moderator links
|
||||
if (participantRole === ParticipantRole.PUBLISHER) {
|
||||
// Remove moderatorRoomUrl if the participant is a speaker to prevent access to moderator links
|
||||
if (participantRole === ParticipantRole.SPEAKER) {
|
||||
delete filteredRoom.moderatorRoomUrl;
|
||||
}
|
||||
|
||||
@ -244,12 +244,12 @@ export class RoomService {
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates a secret against a room's moderator and publisher secrets and returns the corresponding role.
|
||||
* Validates a secret against a room's moderator and speaker secrets and returns the corresponding role.
|
||||
*
|
||||
* @param roomId - The unique identifier of the room to check
|
||||
* @param secret - The secret to validate against the room's moderator and publisher secrets
|
||||
* @returns A promise that resolves to the participant role (MODERATOR or PUBLISHER) if the secret is valid
|
||||
* @throws Error if the moderator or publisher secrets cannot be extracted from their URLs
|
||||
* @param secret - The secret to validate against the room's moderator and speaker secrets
|
||||
* @returns A promise that resolves to the participant role (MODERATOR or SPEAKER) if the secret is valid
|
||||
* @throws Error if the moderator or speaker secrets cannot be extracted from their URLs
|
||||
* @throws Error if the provided secret doesn't match any of the room's secrets (unauthorized)
|
||||
*/
|
||||
async getRoomRoleBySecret(roomId: string, secret: string): Promise<ParticipantRole> {
|
||||
@ -258,13 +258,13 @@ export class RoomService {
|
||||
}
|
||||
|
||||
getRoomRoleBySecretFromRoom(room: MeetRoom, secret: string): ParticipantRole {
|
||||
const { moderatorSecret, publisherSecret } = MeetRoomHelper.extractSecretsFromRoom(room);
|
||||
const { moderatorSecret, speakerSecret } = MeetRoomHelper.extractSecretsFromRoom(room);
|
||||
|
||||
switch (secret) {
|
||||
case moderatorSecret:
|
||||
return ParticipantRole.MODERATOR;
|
||||
case publisherSecret:
|
||||
return ParticipantRole.PUBLISHER;
|
||||
case speakerSecret:
|
||||
return ParticipantRole.SPEAKER;
|
||||
default:
|
||||
throw errorInvalidRoomSecret(room.roomId, secret);
|
||||
}
|
||||
@ -299,11 +299,11 @@ export class RoomService {
|
||||
|
||||
/* A participant can retrieve recordings if
|
||||
- they can delete recordings
|
||||
- they are a publisher and the recording access includes publishers
|
||||
- they are a speaker and the recording access includes speakers
|
||||
*/
|
||||
const canRetrieveRecordings =
|
||||
canDeleteRecordings ||
|
||||
(role === ParticipantRole.PUBLISHER && recordingAccess === MeetRecordingAccess.ADMIN_MODERATOR_PUBLISHER);
|
||||
(role === ParticipantRole.SPEAKER && recordingAccess === MeetRecordingAccess.ADMIN_MODERATOR_SPEAKER);
|
||||
|
||||
return {
|
||||
canRetrieveRecordings,
|
||||
|
||||
@ -278,7 +278,7 @@ export class MeetStorageService<
|
||||
/**
|
||||
* Archives room metadata by storing essential room information in both cache and persistent storage.
|
||||
*
|
||||
* This method retrieves the room data, extracts key metadata (moderator/publisher URLs and
|
||||
* This method retrieves the room data, extracts key metadata (moderator/speaker URLs and
|
||||
* recording preferences), and saves it to an archived location for future reference.
|
||||
*
|
||||
* If `updateOnlyIfExists` is true, it will only save the archived metadata if it already exists,
|
||||
@ -311,7 +311,7 @@ export class MeetStorageService<
|
||||
|
||||
const archivedRoom: Partial<MRoom> = {
|
||||
moderatorRoomUrl: room.moderatorRoomUrl,
|
||||
publisherRoomUrl: room.publisherRoomUrl,
|
||||
speakerRoomUrl: room.speakerRoomUrl,
|
||||
preferences: {
|
||||
recordingPreferences: room.preferences?.recordingPreferences
|
||||
}
|
||||
|
||||
@ -131,7 +131,7 @@ export const expectValidRoom = (
|
||||
expect(room.preferences).toEqual({
|
||||
recordingPreferences: {
|
||||
enabled: true,
|
||||
allowAccessTo: MeetRecordingAccess.ADMIN_MODERATOR_PUBLISHER
|
||||
allowAccessTo: MeetRecordingAccess.ADMIN_MODERATOR_SPEAKER
|
||||
},
|
||||
chatPreferences: { enabled: true },
|
||||
virtualBackgroundPreferences: { enabled: true }
|
||||
@ -139,9 +139,9 @@ export const expectValidRoom = (
|
||||
}
|
||||
|
||||
expect(room.moderatorRoomUrl).toBeDefined();
|
||||
expect(room.publisherRoomUrl).toBeDefined();
|
||||
expect(room.speakerRoomUrl).toBeDefined();
|
||||
expect(room.moderatorRoomUrl).toContain(room.roomId);
|
||||
expect(room.publisherRoomUrl).toContain(room.roomId);
|
||||
expect(room.speakerRoomUrl).toContain(room.roomId);
|
||||
|
||||
if (markedForDeletion !== undefined) {
|
||||
expect(room.autoDeletionDate).toBeDefined();
|
||||
@ -473,8 +473,8 @@ export const expectValidRoomRolesAndPermissionsResponse = (response: any, roomId
|
||||
permissions: getPermissions(roomId, ParticipantRole.MODERATOR)
|
||||
},
|
||||
{
|
||||
role: ParticipantRole.PUBLISHER,
|
||||
permissions: getPermissions(roomId, ParticipantRole.PUBLISHER)
|
||||
role: ParticipantRole.SPEAKER,
|
||||
permissions: getPermissions(roomId, ParticipantRole.SPEAKER)
|
||||
}
|
||||
])
|
||||
);
|
||||
@ -510,7 +510,7 @@ const getPermissions = (roomId: string, role: ParticipantRole, addJoinPermission
|
||||
canChangeVirtualBackground: true
|
||||
}
|
||||
};
|
||||
case ParticipantRole.PUBLISHER:
|
||||
case ParticipantRole.SPEAKER:
|
||||
return {
|
||||
livekit: {
|
||||
roomJoin: addJoinPermission,
|
||||
|
||||
@ -19,8 +19,8 @@ export interface RoomData {
|
||||
room: MeetRoom;
|
||||
moderatorSecret: string;
|
||||
moderatorCookie: string;
|
||||
publisherSecret: string;
|
||||
publisherCookie: string;
|
||||
speakerSecret: string;
|
||||
speakerCookie: string;
|
||||
recordingId?: string;
|
||||
}
|
||||
|
||||
@ -49,10 +49,10 @@ export const setupSingleRoom = async (
|
||||
});
|
||||
|
||||
// Extract the room secrets and generate participant tokens, saved as cookies
|
||||
const { moderatorSecret, publisherSecret } = MeetRoomHelper.extractSecretsFromRoom(room);
|
||||
const [moderatorCookie, publisherCookie] = await Promise.all([
|
||||
const { moderatorSecret, speakerSecret } = MeetRoomHelper.extractSecretsFromRoom(room);
|
||||
const [moderatorCookie, speakerCookie] = await Promise.all([
|
||||
generateParticipantTokenCookie(room.roomId, moderatorSecret, 'MODERATOR'),
|
||||
generateParticipantTokenCookie(room.roomId, publisherSecret, 'PUBLISHER')
|
||||
generateParticipantTokenCookie(room.roomId, speakerSecret, 'SPEAKER')
|
||||
]);
|
||||
|
||||
// Join participant if needed
|
||||
@ -64,8 +64,8 @@ export const setupSingleRoom = async (
|
||||
room,
|
||||
moderatorSecret,
|
||||
moderatorCookie,
|
||||
publisherSecret,
|
||||
publisherCookie
|
||||
speakerSecret,
|
||||
speakerCookie
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
@ -48,40 +48,40 @@ describe('Participant API Tests', () => {
|
||||
);
|
||||
});
|
||||
|
||||
it('should generate a participant token with publisher permissions when using the publisher secret', async () => {
|
||||
it('should generate a participant token with speaker permissions when using the speaker secret', async () => {
|
||||
const response = await generateParticipantToken({
|
||||
roomId: roomData.room.roomId,
|
||||
secret: roomData.publisherSecret,
|
||||
secret: roomData.speakerSecret,
|
||||
participantName
|
||||
});
|
||||
expectValidParticipantTokenResponse(
|
||||
response,
|
||||
roomData.room.roomId,
|
||||
ParticipantRole.PUBLISHER,
|
||||
ParticipantRole.SPEAKER,
|
||||
participantName
|
||||
);
|
||||
});
|
||||
|
||||
it(`should generate a participant token with both publisher and moderator permissions
|
||||
when using the publisher secret after having a moderator token`, async () => {
|
||||
it(`should generate a participant token with both speaker and moderator permissions
|
||||
when using the speaker secret after having a moderator token`, async () => {
|
||||
const moderatorCookie = await generateParticipantTokenCookie(
|
||||
roomData.room.roomId,
|
||||
roomData.moderatorSecret,
|
||||
`${participantName}_MODERATOR`
|
||||
);
|
||||
const publisherResponse = await generateParticipantToken(
|
||||
const speakerResponse = await generateParticipantToken(
|
||||
{
|
||||
roomId: roomData.room.roomId,
|
||||
secret: roomData.publisherSecret,
|
||||
participantName: `${participantName}_PUBLISHER`
|
||||
secret: roomData.speakerSecret,
|
||||
participantName: `${participantName}_SPEAKER`
|
||||
},
|
||||
moderatorCookie
|
||||
);
|
||||
expectValidParticipantTokenResponse(
|
||||
publisherResponse,
|
||||
speakerResponse,
|
||||
roomData.room.roomId,
|
||||
ParticipantRole.PUBLISHER,
|
||||
`${participantName}_PUBLISHER`,
|
||||
ParticipantRole.SPEAKER,
|
||||
`${participantName}_SPEAKER`,
|
||||
[ParticipantRole.MODERATOR]
|
||||
);
|
||||
});
|
||||
|
||||
@ -53,19 +53,19 @@ describe('Participant API Tests', () => {
|
||||
);
|
||||
});
|
||||
|
||||
it('should refresh participant token with publisher permissions when using the publisher secret', async () => {
|
||||
it('should refresh participant token with speaker permissions when using the speaker secret', async () => {
|
||||
const response = await refreshParticipantToken(
|
||||
{
|
||||
roomId: roomData.room.roomId,
|
||||
secret: roomData.publisherSecret,
|
||||
secret: roomData.speakerSecret,
|
||||
participantName
|
||||
},
|
||||
roomData.publisherCookie
|
||||
roomData.speakerCookie
|
||||
);
|
||||
expectValidParticipantTokenResponse(
|
||||
response,
|
||||
roomData.room.roomId,
|
||||
ParticipantRole.PUBLISHER,
|
||||
ParticipantRole.SPEAKER,
|
||||
participantName
|
||||
);
|
||||
});
|
||||
|
||||
@ -182,13 +182,13 @@ describe('Recording API Tests', () => {
|
||||
|
||||
expect(roomMetadata).toBeDefined();
|
||||
expect(roomMetadata!.moderatorRoomUrl).toContain(room.roomId);
|
||||
expect(roomMetadata!.publisherRoomUrl).toContain(room.roomId);
|
||||
expect(roomMetadata!.speakerRoomUrl).toContain(room.roomId);
|
||||
|
||||
roomMetadata = await meetStorageService.getArchivedRoomMetadata(room.roomId);
|
||||
|
||||
expect(roomMetadata).toBeDefined();
|
||||
expect(roomMetadata!.moderatorRoomUrl).toContain(room.roomId);
|
||||
expect(roomMetadata!.publisherRoomUrl).toContain(room.roomId);
|
||||
expect(roomMetadata!.speakerRoomUrl).toContain(room.roomId);
|
||||
|
||||
const response = await startRecording(room.roomId, moderatorCookie);
|
||||
expectValidStartRecordingResponse(response, room.roomId, room.roomName);
|
||||
|
||||
@ -74,7 +74,7 @@ describe('Recording API Tests', () => {
|
||||
let roomMetadata = await meetStorageService.getArchivedRoomMetadata(room.roomId);
|
||||
expect(roomMetadata).toBeDefined();
|
||||
expect(roomMetadata!.moderatorRoomUrl).toContain(room.roomId);
|
||||
expect(roomMetadata!.publisherRoomUrl).toContain(room.roomId);
|
||||
expect(roomMetadata!.speakerRoomUrl).toContain(room.roomId);
|
||||
|
||||
// Generate a new recording
|
||||
const response = await startRecording(room.roomId, moderatorCookie);
|
||||
@ -91,7 +91,7 @@ describe('Recording API Tests', () => {
|
||||
|
||||
expect(roomMetadata).toBeDefined();
|
||||
expect(roomMetadata!.moderatorRoomUrl).toContain(room.roomId);
|
||||
expect(roomMetadata!.publisherRoomUrl).toContain(room.roomId);
|
||||
expect(roomMetadata!.speakerRoomUrl).toContain(room.roomId);
|
||||
|
||||
// Delete the second recording
|
||||
deleteResponse = await deleteRecording(secondRecordingId!);
|
||||
|
||||
@ -63,7 +63,7 @@ describe('Recordings API Tests', () => {
|
||||
const roomId = roomData.room.roomId;
|
||||
|
||||
// Generate a recording token for the room
|
||||
const recordingCookie = await generateRecordingTokenCookie(roomId, roomData.publisherSecret);
|
||||
const recordingCookie = await generateRecordingTokenCookie(roomId, roomData.speakerSecret);
|
||||
|
||||
// Create a new room and start a recording
|
||||
roomData = await setupSingleRoomWithRecording(true);
|
||||
|
||||
@ -72,7 +72,7 @@ describe('Recording API Tests', () => {
|
||||
const archivedRoom = await storageService.getArchivedRoomMetadata(room.roomId);
|
||||
expect(archivedRoom).toBeDefined();
|
||||
expect(archivedRoom?.moderatorRoomUrl).toBeDefined();
|
||||
expect(archivedRoom?.publisherRoomUrl).toBeDefined();
|
||||
expect(archivedRoom?.speakerRoomUrl).toBeDefined();
|
||||
expect(archivedRoom?.preferences).toBeDefined();
|
||||
|
||||
const secretsResponse = await stopRecording(recordingId, moderatorCookie);
|
||||
|
||||
@ -48,7 +48,7 @@ describe('Room API Tests', () => {
|
||||
preferences: {
|
||||
recordingPreferences: {
|
||||
enabled: false,
|
||||
allowAccessTo: MeetRecordingAccess.ADMIN_MODERATOR_PUBLISHER
|
||||
allowAccessTo: MeetRecordingAccess.ADMIN_MODERATOR_SPEAKER
|
||||
},
|
||||
chatPreferences: { enabled: false },
|
||||
virtualBackgroundPreferences: { enabled: true }
|
||||
@ -165,7 +165,7 @@ describe('Room API Tests', () => {
|
||||
preferences: {
|
||||
recordingPreferences: {
|
||||
enabled: 'yes', // invalid boolean
|
||||
allowAccessTo: MeetRecordingAccess.ADMIN_MODERATOR_PUBLISHER
|
||||
allowAccessTo: MeetRecordingAccess.ADMIN_MODERATOR_SPEAKER
|
||||
},
|
||||
chatPreferences: { enabled: true },
|
||||
virtualBackgroundPreferences: { enabled: true }
|
||||
|
||||
@ -34,35 +34,38 @@ describe('Room API Tests', () => {
|
||||
expectValidRecordingTokenResponse(response, roomData.room.roomId, ParticipantRole.MODERATOR, true, true);
|
||||
});
|
||||
|
||||
it('should generate a recording token with canRetrieve and canDelete permissions when using the moderator secret and recording access is admin-moderator-publisher', async () => {
|
||||
it('should generate a recording token with canRetrieve and canDelete permissions when using the moderator secret and recording access is admin-moderator-speaker', async () => {
|
||||
await updateRecordingAccessPreferencesInRoom(
|
||||
roomData.room.roomId,
|
||||
MeetRecordingAccess.ADMIN_MODERATOR_PUBLISHER
|
||||
MeetRecordingAccess.ADMIN_MODERATOR_SPEAKER
|
||||
);
|
||||
|
||||
const response = await generateRecordingToken(roomData.room.roomId, roomData.moderatorSecret);
|
||||
expectValidRecordingTokenResponse(response, roomData.room.roomId, ParticipantRole.MODERATOR, true, true);
|
||||
});
|
||||
|
||||
it('should generate a recording token without any permissions when using the publisher secret and recording access is admin-moderator', async () => {
|
||||
it('should generate a recording token without any permissions when using the speaker secret and recording access is admin-moderator', async () => {
|
||||
await updateRecordingAccessPreferencesInRoom(roomData.room.roomId, MeetRecordingAccess.ADMIN_MODERATOR);
|
||||
|
||||
const response = await generateRecordingToken(roomData.room.roomId, roomData.publisherSecret);
|
||||
expectValidRecordingTokenResponse(response, roomData.room.roomId, ParticipantRole.PUBLISHER, false, false);
|
||||
const response = await generateRecordingToken(roomData.room.roomId, roomData.speakerSecret);
|
||||
expectValidRecordingTokenResponse(response, roomData.room.roomId, ParticipantRole.SPEAKER, false, false);
|
||||
});
|
||||
|
||||
it('should generate a recording token with canRetrieve permission but not canDelete when using the publisher secret and recording access is admin-moderator-publisher', async () => {
|
||||
it('should generate a recording token with canRetrieve permission but not canDelete when using the speaker secret and recording access is admin-moderator-speaker', async () => {
|
||||
await updateRecordingAccessPreferencesInRoom(
|
||||
roomData.room.roomId,
|
||||
MeetRecordingAccess.ADMIN_MODERATOR_PUBLISHER
|
||||
MeetRecordingAccess.ADMIN_MODERATOR_SPEAKER
|
||||
);
|
||||
|
||||
const response = await generateRecordingToken(roomData.room.roomId, roomData.publisherSecret);
|
||||
expectValidRecordingTokenResponse(response, roomData.room.roomId, ParticipantRole.PUBLISHER, true, false);
|
||||
const response = await generateRecordingToken(roomData.room.roomId, roomData.speakerSecret);
|
||||
expectValidRecordingTokenResponse(response, roomData.room.roomId, ParticipantRole.SPEAKER, true, false);
|
||||
});
|
||||
|
||||
it('should succeed even if the room is deleted', async () => {
|
||||
await updateRecordingAccessPreferencesInRoom(roomData.room.roomId, MeetRecordingAccess.ADMIN_MODERATOR_PUBLISHER);
|
||||
await updateRecordingAccessPreferencesInRoom(
|
||||
roomData.room.roomId,
|
||||
MeetRecordingAccess.ADMIN_MODERATOR_SPEAKER
|
||||
);
|
||||
await deleteRoom(roomData.room.roomId);
|
||||
|
||||
const response = await generateRecordingToken(roomData.room.roomId, roomData.moderatorSecret);
|
||||
|
||||
@ -8,7 +8,7 @@ describe('Room API Tests', () => {
|
||||
const DEFAULT_PREFERENCES = {
|
||||
recordingPreferences: {
|
||||
enabled: true,
|
||||
allowAccessTo: MeetRecordingAccess.ADMIN_MODERATOR_PUBLISHER
|
||||
allowAccessTo: MeetRecordingAccess.ADMIN_MODERATOR_SPEAKER
|
||||
},
|
||||
chatPreferences: { enabled: true },
|
||||
virtualBackgroundPreferences: { enabled: true }
|
||||
@ -39,7 +39,7 @@ describe('Room API Tests', () => {
|
||||
preferences: {
|
||||
recordingPreferences: {
|
||||
enabled: true,
|
||||
allowAccessTo: MeetRecordingAccess.ADMIN_MODERATOR_PUBLISHER
|
||||
allowAccessTo: MeetRecordingAccess.ADMIN_MODERATOR_SPEAKER
|
||||
},
|
||||
chatPreferences: { enabled: true },
|
||||
virtualBackgroundPreferences: { enabled: false }
|
||||
|
||||
@ -42,9 +42,9 @@ describe('Room API Tests', () => {
|
||||
expectValidRoomRoleAndPermissionsResponse(response, roomData.room.roomId, ParticipantRole.MODERATOR);
|
||||
});
|
||||
|
||||
it('should retrieve publisher role and associated permissions for a room with a valid publisher secret', async () => {
|
||||
const response = await getRoomRoleBySecret(roomData.room.roomId, roomData.publisherSecret);
|
||||
expectValidRoomRoleAndPermissionsResponse(response, roomData.room.roomId, ParticipantRole.PUBLISHER);
|
||||
it('should retrieve speaker role and associated permissions for a room with a valid speaker secret', async () => {
|
||||
const response = await getRoomRoleBySecret(roomData.room.roomId, roomData.speakerSecret);
|
||||
expectValidRoomRoleAndPermissionsResponse(response, roomData.room.roomId, ParticipantRole.SPEAKER);
|
||||
});
|
||||
|
||||
it('should return a 404 error if the room does not exist', async () => {
|
||||
|
||||
@ -38,7 +38,7 @@ describe('Room API Tests', () => {
|
||||
preferences: {
|
||||
recordingPreferences: {
|
||||
enabled: true,
|
||||
allowAccessTo: MeetRecordingAccess.ADMIN_MODERATOR_PUBLISHER
|
||||
allowAccessTo: MeetRecordingAccess.ADMIN_MODERATOR_SPEAKER
|
||||
},
|
||||
chatPreferences: { enabled: true },
|
||||
virtualBackgroundPreferences: { enabled: false }
|
||||
@ -97,13 +97,13 @@ describe('Room API Tests', () => {
|
||||
expectSuccessRoomResponse(response, 'deletion-date', validAutoDeletionDate);
|
||||
});
|
||||
|
||||
it('should retrieve a room without moderatorRoomUrl when participant is publisher', async () => {
|
||||
it('should retrieve a room without moderatorRoomUrl when participant is speaker', async () => {
|
||||
const roomData = await setupSingleRoom();
|
||||
const response = await getRoom(
|
||||
roomData.room.roomId,
|
||||
undefined,
|
||||
roomData.publisherCookie,
|
||||
ParticipantRole.PUBLISHER
|
||||
roomData.speakerCookie,
|
||||
ParticipantRole.SPEAKER
|
||||
);
|
||||
expect(response.status).toBe(200);
|
||||
expect(response.body.moderatorRoomUrl).toBeUndefined();
|
||||
|
||||
@ -36,7 +36,7 @@ describe('Room API Tests', () => {
|
||||
preferences: {
|
||||
recordingPreferences: {
|
||||
enabled: true,
|
||||
allowAccessTo: MeetRecordingAccess.ADMIN_MODERATOR_PUBLISHER
|
||||
allowAccessTo: MeetRecordingAccess.ADMIN_MODERATOR_SPEAKER
|
||||
},
|
||||
chatPreferences: { enabled: true },
|
||||
virtualBackgroundPreferences: { enabled: true }
|
||||
@ -85,7 +85,7 @@ describe('Room API Tests', () => {
|
||||
preferences: {
|
||||
recordingPreferences: {
|
||||
enabled: true,
|
||||
allowAccessTo: MeetRecordingAccess.ADMIN_MODERATOR_PUBLISHER
|
||||
allowAccessTo: MeetRecordingAccess.ADMIN_MODERATOR_SPEAKER
|
||||
},
|
||||
chatPreferences: { enabled: true },
|
||||
virtualBackgroundPreferences: { enabled: true }
|
||||
@ -96,7 +96,7 @@ describe('Room API Tests', () => {
|
||||
const partialPreferences = {
|
||||
recordingPreferences: {
|
||||
enabled: false,
|
||||
allowAccessTo: MeetRecordingAccess.ADMIN_MODERATOR_PUBLISHER
|
||||
allowAccessTo: MeetRecordingAccess.ADMIN_MODERATOR_SPEAKER
|
||||
},
|
||||
chatPreferences: { enabled: true },
|
||||
virtualBackgroundPreferences: { enabled: true }
|
||||
@ -146,7 +146,7 @@ describe('Room API Tests', () => {
|
||||
const invalidPreferences = {
|
||||
recordingPreferences: {
|
||||
enabled: 'true', // String instead of boolean
|
||||
allowAccessTo: MeetRecordingAccess.ADMIN_MODERATOR_PUBLISHER
|
||||
allowAccessTo: MeetRecordingAccess.ADMIN_MODERATOR_SPEAKER
|
||||
},
|
||||
chatPreferences: { enabled: false },
|
||||
virtualBackgroundPreferences: { enabled: false }
|
||||
@ -212,7 +212,7 @@ describe('Room API Tests', () => {
|
||||
const preferences = {
|
||||
recordingPreferences: {
|
||||
enabled: false,
|
||||
allowAccessTo: MeetRecordingAccess.ADMIN_MODERATOR_PUBLISHER
|
||||
allowAccessTo: MeetRecordingAccess.ADMIN_MODERATOR_SPEAKER
|
||||
},
|
||||
chatPreferences: { enabled: false },
|
||||
virtualBackgroundPreferences: { enabled: false }
|
||||
|
||||
@ -66,11 +66,11 @@ describe('Meeting API Security Tests', () => {
|
||||
expect(response.status).toBe(403);
|
||||
});
|
||||
|
||||
it('should fail when participant is publisher', async () => {
|
||||
it('should fail when participant is speaker', async () => {
|
||||
const response = await request(app)
|
||||
.delete(`${MEETINGS_PATH}/${roomData.room.roomId}`)
|
||||
.set('Cookie', roomData.publisherCookie)
|
||||
.set(INTERNAL_CONFIG.PARTICIPANT_ROLE_HEADER, ParticipantRole.PUBLISHER);
|
||||
.set('Cookie', roomData.speakerCookie)
|
||||
.set(INTERNAL_CONFIG.PARTICIPANT_ROLE_HEADER, ParticipantRole.SPEAKER);
|
||||
expect(response.status).toBe(403);
|
||||
});
|
||||
});
|
||||
@ -110,11 +110,11 @@ describe('Meeting API Security Tests', () => {
|
||||
expect(response.status).toBe(403);
|
||||
});
|
||||
|
||||
it('should fail when participant is publisher', async () => {
|
||||
it('should fail when participant is speaker', async () => {
|
||||
const response = await request(app)
|
||||
.delete(`${MEETINGS_PATH}/${roomData.room.roomId}/participants/${PARTICIPANT_NAME}`)
|
||||
.set('Cookie', roomData.publisherCookie)
|
||||
.set(INTERNAL_CONFIG.PARTICIPANT_ROLE_HEADER, ParticipantRole.PUBLISHER);
|
||||
.set('Cookie', roomData.speakerCookie)
|
||||
.set(INTERNAL_CONFIG.PARTICIPANT_ROLE_HEADER, ParticipantRole.SPEAKER);
|
||||
expect(response.status).toBe(403);
|
||||
});
|
||||
});
|
||||
|
||||
@ -38,12 +38,12 @@ describe('Participant API Security Tests', () => {
|
||||
roomData = await setupSingleRoom();
|
||||
});
|
||||
|
||||
it('should succeed when no authentication is required and participant is publisher', async () => {
|
||||
it('should succeed when no authentication is required and participant is speaker', async () => {
|
||||
await changeSecurityPreferences(AuthMode.NONE);
|
||||
|
||||
const response = await request(app).post(`${PARTICIPANTS_PATH}/token`).send({
|
||||
roomId: roomData.room.roomId,
|
||||
secret: roomData.publisherSecret,
|
||||
secret: roomData.speakerSecret,
|
||||
participantName: PARTICIPANT_NAME
|
||||
});
|
||||
expect(response.status).toBe(200);
|
||||
@ -60,12 +60,12 @@ describe('Participant API Security Tests', () => {
|
||||
expect(response.status).toBe(200);
|
||||
});
|
||||
|
||||
it('should succeed when authentication is required for moderator and participant is publisher', async () => {
|
||||
it('should succeed when authentication is required for moderator and participant is speaker', async () => {
|
||||
await changeSecurityPreferences(AuthMode.MODERATORS_ONLY);
|
||||
|
||||
const response = await request(app).post(`${PARTICIPANTS_PATH}/token`).send({
|
||||
roomId: roomData.room.roomId,
|
||||
secret: roomData.publisherSecret,
|
||||
secret: roomData.speakerSecret,
|
||||
participantName: PARTICIPANT_NAME
|
||||
});
|
||||
expect(response.status).toBe(200);
|
||||
@ -93,23 +93,23 @@ describe('Participant API Security Tests', () => {
|
||||
expect(response.status).toBe(401);
|
||||
});
|
||||
|
||||
it('should succeed when authentication is required for all users, participant is publisher and authenticated', async () => {
|
||||
it('should succeed when authentication is required for all users, participant is speaker and authenticated', async () => {
|
||||
await changeSecurityPreferences(AuthMode.ALL_USERS);
|
||||
|
||||
const response = await request(app).post(`${PARTICIPANTS_PATH}/token`).set('Cookie', adminCookie).send({
|
||||
roomId: roomData.room.roomId,
|
||||
secret: roomData.publisherSecret,
|
||||
secret: roomData.speakerSecret,
|
||||
participantName: PARTICIPANT_NAME
|
||||
});
|
||||
expect(response.status).toBe(200);
|
||||
});
|
||||
|
||||
it('should fail when authentication is required for all users and participant is publisher but not authenticated', async () => {
|
||||
it('should fail when authentication is required for all users and participant is speaker but not authenticated', async () => {
|
||||
await changeSecurityPreferences(AuthMode.ALL_USERS);
|
||||
|
||||
const response = await request(app).post(`${PARTICIPANTS_PATH}/token`).send({
|
||||
roomId: roomData.room.roomId,
|
||||
secret: roomData.publisherSecret,
|
||||
secret: roomData.speakerSecret,
|
||||
participantName: PARTICIPANT_NAME
|
||||
});
|
||||
expect(response.status).toBe(401);
|
||||
@ -153,15 +153,15 @@ describe('Participant API Security Tests', () => {
|
||||
INTERNAL_CONFIG.PARTICIPANT_TOKEN_EXPIRATION = initialTokenExpiration;
|
||||
});
|
||||
|
||||
it('should succeed when no authentication is required and participant is publisher', async () => {
|
||||
it('should succeed when no authentication is required and participant is speaker', async () => {
|
||||
await changeSecurityPreferences(AuthMode.NONE);
|
||||
|
||||
const response = await request(app)
|
||||
.post(`${PARTICIPANTS_PATH}/token/refresh`)
|
||||
.set('Cookie', roomData.publisherCookie)
|
||||
.set('Cookie', roomData.speakerCookie)
|
||||
.send({
|
||||
roomId: roomData.room.roomId,
|
||||
secret: roomData.publisherSecret,
|
||||
secret: roomData.speakerSecret,
|
||||
participantName: PARTICIPANT_NAME
|
||||
});
|
||||
expect(response.status).toBe(200);
|
||||
@ -181,15 +181,15 @@ describe('Participant API Security Tests', () => {
|
||||
expect(response.status).toBe(200);
|
||||
});
|
||||
|
||||
it('should succeed when authentication is required for moderator and participant is publisher', async () => {
|
||||
it('should succeed when authentication is required for moderator and participant is speaker', async () => {
|
||||
await changeSecurityPreferences(AuthMode.MODERATORS_ONLY);
|
||||
|
||||
const response = await request(app)
|
||||
.post(`${PARTICIPANTS_PATH}/token/refresh`)
|
||||
.set('Cookie', roomData.publisherCookie)
|
||||
.set('Cookie', roomData.speakerCookie)
|
||||
.send({
|
||||
roomId: roomData.room.roomId,
|
||||
secret: roomData.publisherSecret,
|
||||
secret: roomData.speakerSecret,
|
||||
participantName: PARTICIPANT_NAME
|
||||
});
|
||||
expect(response.status).toBe(200);
|
||||
@ -223,29 +223,29 @@ describe('Participant API Security Tests', () => {
|
||||
expect(response.status).toBe(401);
|
||||
});
|
||||
|
||||
it('should succeed when authentication is required for all users, participant is publisher and authenticated', async () => {
|
||||
it('should succeed when authentication is required for all users, participant is speaker and authenticated', async () => {
|
||||
await changeSecurityPreferences(AuthMode.ALL_USERS);
|
||||
|
||||
const response = await request(app)
|
||||
.post(`${PARTICIPANTS_PATH}/token/refresh`)
|
||||
.set('Cookie', [adminCookie, roomData.publisherCookie])
|
||||
.set('Cookie', [adminCookie, roomData.speakerCookie])
|
||||
.send({
|
||||
roomId: roomData.room.roomId,
|
||||
secret: roomData.publisherSecret,
|
||||
secret: roomData.speakerSecret,
|
||||
participantName: PARTICIPANT_NAME
|
||||
});
|
||||
expect(response.status).toBe(200);
|
||||
});
|
||||
|
||||
it('should fail when authentication is required for all users and participant is publisher but not authenticated', async () => {
|
||||
it('should fail when authentication is required for all users and participant is speaker but not authenticated', async () => {
|
||||
await changeSecurityPreferences(AuthMode.ALL_USERS);
|
||||
|
||||
const response = await request(app)
|
||||
.post(`${PARTICIPANTS_PATH}/token/refresh`)
|
||||
.set('Cookie', roomData.publisherCookie)
|
||||
.set('Cookie', roomData.speakerCookie)
|
||||
.send({
|
||||
roomId: roomData.room.roomId,
|
||||
secret: roomData.publisherSecret,
|
||||
secret: roomData.speakerSecret,
|
||||
participantName: PARTICIPANT_NAME
|
||||
});
|
||||
expect(response.status).toBe(401);
|
||||
|
||||
@ -84,12 +84,12 @@ describe('Recording API Security Tests', () => {
|
||||
expect(response.status).toBe(403);
|
||||
});
|
||||
|
||||
it('should fail when participant is publisher', async () => {
|
||||
it('should fail when participant is speaker', async () => {
|
||||
const response = await request(app)
|
||||
.post(INTERNAL_RECORDINGS_PATH)
|
||||
.send({ roomId: roomData.room.roomId })
|
||||
.set('Cookie', roomData.publisherCookie)
|
||||
.set(INTERNAL_CONFIG.PARTICIPANT_ROLE_HEADER, ParticipantRole.PUBLISHER);
|
||||
.set('Cookie', roomData.speakerCookie)
|
||||
.set(INTERNAL_CONFIG.PARTICIPANT_ROLE_HEADER, ParticipantRole.SPEAKER);
|
||||
expect(response.status).toBe(403);
|
||||
});
|
||||
});
|
||||
@ -138,11 +138,11 @@ describe('Recording API Security Tests', () => {
|
||||
expect(response.status).toBe(403);
|
||||
});
|
||||
|
||||
it('should fail when participant is publisher', async () => {
|
||||
it('should fail when participant is speaker', async () => {
|
||||
const response = await request(app)
|
||||
.post(`${INTERNAL_RECORDINGS_PATH}/${roomData.recordingId}/stop`)
|
||||
.set('Cookie', roomData.publisherCookie)
|
||||
.set(INTERNAL_CONFIG.PARTICIPANT_ROLE_HEADER, ParticipantRole.PUBLISHER);
|
||||
.set('Cookie', roomData.speakerCookie)
|
||||
.set(INTERNAL_CONFIG.PARTICIPANT_ROLE_HEADER, ParticipantRole.SPEAKER);
|
||||
expect(response.status).toBe(403);
|
||||
});
|
||||
});
|
||||
@ -169,24 +169,24 @@ describe('Recording API Security Tests', () => {
|
||||
expect(response.status).toBe(200);
|
||||
});
|
||||
|
||||
it('should succeed when recording access is admin-moderator-publisher and participant is publisher', async () => {
|
||||
it('should succeed when recording access is admin-moderator-speaker and participant is speaker', async () => {
|
||||
await updateRecordingAccessPreferencesInRoom(
|
||||
roomData.room.roomId,
|
||||
MeetRecordingAccess.ADMIN_MODERATOR_PUBLISHER
|
||||
MeetRecordingAccess.ADMIN_MODERATOR_SPEAKER
|
||||
);
|
||||
const recordingCookie = await generateRecordingTokenCookie(
|
||||
roomData.room.roomId,
|
||||
roomData.publisherSecret
|
||||
roomData.speakerSecret
|
||||
);
|
||||
|
||||
const response = await request(app).get(RECORDINGS_PATH).set('Cookie', recordingCookie);
|
||||
expect(response.status).toBe(200);
|
||||
});
|
||||
|
||||
it('should succeed when recording access is admin-moderator-publisher and participant is moderator', async () => {
|
||||
it('should succeed when recording access is admin-moderator-speaker and participant is moderator', async () => {
|
||||
await updateRecordingAccessPreferencesInRoom(
|
||||
roomData.room.roomId,
|
||||
MeetRecordingAccess.ADMIN_MODERATOR_PUBLISHER
|
||||
MeetRecordingAccess.ADMIN_MODERATOR_SPEAKER
|
||||
);
|
||||
const recordingCookie = await generateRecordingTokenCookie(
|
||||
roomData.room.roomId,
|
||||
@ -197,11 +197,11 @@ describe('Recording API Security Tests', () => {
|
||||
expect(response.status).toBe(200);
|
||||
});
|
||||
|
||||
it('should fail when recording access is admin-moderator and participant is publisher', async () => {
|
||||
it('should fail when recording access is admin-moderator and participant is speaker', async () => {
|
||||
await updateRecordingAccessPreferencesInRoom(roomData.room.roomId, MeetRecordingAccess.ADMIN_MODERATOR);
|
||||
const recordingCookie = await generateRecordingTokenCookie(
|
||||
roomData.room.roomId,
|
||||
roomData.publisherSecret
|
||||
roomData.speakerSecret
|
||||
);
|
||||
|
||||
const response = await request(app).get(RECORDINGS_PATH).set('Cookie', recordingCookie);
|
||||
@ -233,14 +233,14 @@ describe('Recording API Security Tests', () => {
|
||||
expect(response.status).toBe(200);
|
||||
});
|
||||
|
||||
it('should succeed when recording access is admin-moderator-publisher and participant is publisher', async () => {
|
||||
it('should succeed when recording access is admin-moderator-speaker and participant is speaker', async () => {
|
||||
await updateRecordingAccessPreferencesInRoom(
|
||||
roomData.room.roomId,
|
||||
MeetRecordingAccess.ADMIN_MODERATOR_PUBLISHER
|
||||
MeetRecordingAccess.ADMIN_MODERATOR_SPEAKER
|
||||
);
|
||||
const recordingCookie = await generateRecordingTokenCookie(
|
||||
roomData.room.roomId,
|
||||
roomData.publisherSecret
|
||||
roomData.speakerSecret
|
||||
);
|
||||
|
||||
const response = await request(app)
|
||||
@ -249,10 +249,10 @@ describe('Recording API Security Tests', () => {
|
||||
expect(response.status).toBe(200);
|
||||
});
|
||||
|
||||
it('should succeed when recording access is admin-moderator-publisher and participant is moderator', async () => {
|
||||
it('should succeed when recording access is admin-moderator-speaker and participant is moderator', async () => {
|
||||
await updateRecordingAccessPreferencesInRoom(
|
||||
roomData.room.roomId,
|
||||
MeetRecordingAccess.ADMIN_MODERATOR_PUBLISHER
|
||||
MeetRecordingAccess.ADMIN_MODERATOR_SPEAKER
|
||||
);
|
||||
const recordingCookie = await generateRecordingTokenCookie(
|
||||
roomData.room.roomId,
|
||||
@ -265,11 +265,11 @@ describe('Recording API Security Tests', () => {
|
||||
expect(response.status).toBe(200);
|
||||
});
|
||||
|
||||
it('should fail when recording access is admin-moderator and participant is publisher', async () => {
|
||||
it('should fail when recording access is admin-moderator and participant is speaker', async () => {
|
||||
await updateRecordingAccessPreferencesInRoom(roomData.room.roomId, MeetRecordingAccess.ADMIN_MODERATOR);
|
||||
const recordingCookie = await generateRecordingTokenCookie(
|
||||
roomData.room.roomId,
|
||||
roomData.publisherSecret
|
||||
roomData.speakerSecret
|
||||
);
|
||||
|
||||
const response = await request(app)
|
||||
@ -362,14 +362,14 @@ describe('Recording API Security Tests', () => {
|
||||
expect(response.status).toBe(404);
|
||||
});
|
||||
|
||||
it('should fail when recording access is admin-moderator-publisher and participant is publisher', async () => {
|
||||
it('should fail when recording access is admin-moderator-speaker and participant is speaker', async () => {
|
||||
await updateRecordingAccessPreferencesInRoom(
|
||||
roomData.room.roomId,
|
||||
MeetRecordingAccess.ADMIN_MODERATOR_PUBLISHER
|
||||
MeetRecordingAccess.ADMIN_MODERATOR_SPEAKER
|
||||
);
|
||||
const recordingCookie = await generateRecordingTokenCookie(
|
||||
roomData.room.roomId,
|
||||
roomData.publisherSecret
|
||||
roomData.speakerSecret
|
||||
);
|
||||
|
||||
const response = await request(app)
|
||||
@ -378,10 +378,10 @@ describe('Recording API Security Tests', () => {
|
||||
expect(response.status).toBe(403);
|
||||
});
|
||||
|
||||
it('should succeed when recording access is admin-moderator-publisher and participant is moderator', async () => {
|
||||
it('should succeed when recording access is admin-moderator-speaker and participant is moderator', async () => {
|
||||
await updateRecordingAccessPreferencesInRoom(
|
||||
roomData.room.roomId,
|
||||
MeetRecordingAccess.ADMIN_MODERATOR_PUBLISHER
|
||||
MeetRecordingAccess.ADMIN_MODERATOR_SPEAKER
|
||||
);
|
||||
const recordingCookie = await generateRecordingTokenCookie(
|
||||
roomData.room.roomId,
|
||||
@ -394,11 +394,11 @@ describe('Recording API Security Tests', () => {
|
||||
expect(response.status).toBe(404);
|
||||
});
|
||||
|
||||
it('should fail when recording access is admin-moderator and participant is publisher', async () => {
|
||||
it('should fail when recording access is admin-moderator and participant is speaker', async () => {
|
||||
await updateRecordingAccessPreferencesInRoom(roomData.room.roomId, MeetRecordingAccess.ADMIN_MODERATOR);
|
||||
const recordingCookie = await generateRecordingTokenCookie(
|
||||
roomData.room.roomId,
|
||||
roomData.publisherSecret
|
||||
roomData.speakerSecret
|
||||
);
|
||||
|
||||
const response = await request(app)
|
||||
@ -449,14 +449,14 @@ describe('Recording API Security Tests', () => {
|
||||
expect(response.status).toBe(200);
|
||||
});
|
||||
|
||||
it('should fail when recording access is admin-moderator-publisher and participant is publisher', async () => {
|
||||
it('should fail when recording access is admin-moderator-speaker and participant is speaker', async () => {
|
||||
await updateRecordingAccessPreferencesInRoom(
|
||||
roomData.room.roomId,
|
||||
MeetRecordingAccess.ADMIN_MODERATOR_PUBLISHER
|
||||
MeetRecordingAccess.ADMIN_MODERATOR_SPEAKER
|
||||
);
|
||||
const recordingCookie = await generateRecordingTokenCookie(
|
||||
roomData.room.roomId,
|
||||
roomData.publisherSecret
|
||||
roomData.speakerSecret
|
||||
);
|
||||
|
||||
const response = await request(app)
|
||||
@ -466,10 +466,10 @@ describe('Recording API Security Tests', () => {
|
||||
expect(response.status).toBe(403);
|
||||
});
|
||||
|
||||
it('should succeed when recording access is admin-moderator-publisher and participant is moderator', async () => {
|
||||
it('should succeed when recording access is admin-moderator-speaker and participant is moderator', async () => {
|
||||
await updateRecordingAccessPreferencesInRoom(
|
||||
roomData.room.roomId,
|
||||
MeetRecordingAccess.ADMIN_MODERATOR_PUBLISHER
|
||||
MeetRecordingAccess.ADMIN_MODERATOR_SPEAKER
|
||||
);
|
||||
const recordingCookie = await generateRecordingTokenCookie(
|
||||
roomData.room.roomId,
|
||||
@ -483,11 +483,11 @@ describe('Recording API Security Tests', () => {
|
||||
expect(response.status).toBe(200);
|
||||
});
|
||||
|
||||
it('should fail when recording access is admin-moderator and participant is publisher', async () => {
|
||||
it('should fail when recording access is admin-moderator and participant is speaker', async () => {
|
||||
await updateRecordingAccessPreferencesInRoom(roomData.room.roomId, MeetRecordingAccess.ADMIN_MODERATOR);
|
||||
const recordingCookie = await generateRecordingTokenCookie(
|
||||
roomData.room.roomId,
|
||||
roomData.publisherSecret
|
||||
roomData.speakerSecret
|
||||
);
|
||||
|
||||
const response = await request(app)
|
||||
@ -527,14 +527,14 @@ describe('Recording API Security Tests', () => {
|
||||
expect(response.status).toBe(200);
|
||||
});
|
||||
|
||||
it('should succeed when recording access is admin-moderator-publisher and participant is publisher', async () => {
|
||||
it('should succeed when recording access is admin-moderator-speaker and participant is speaker', async () => {
|
||||
await updateRecordingAccessPreferencesInRoom(
|
||||
roomData.room.roomId,
|
||||
MeetRecordingAccess.ADMIN_MODERATOR_PUBLISHER
|
||||
MeetRecordingAccess.ADMIN_MODERATOR_SPEAKER
|
||||
);
|
||||
const recordingCookie = await generateRecordingTokenCookie(
|
||||
roomData.room.roomId,
|
||||
roomData.publisherSecret
|
||||
roomData.speakerSecret
|
||||
);
|
||||
|
||||
const response = await request(app)
|
||||
@ -543,10 +543,10 @@ describe('Recording API Security Tests', () => {
|
||||
expect(response.status).toBe(200);
|
||||
});
|
||||
|
||||
it('should succeed when recording access is admin-moderator-publisher and participant is moderator', async () => {
|
||||
it('should succeed when recording access is admin-moderator-speaker and participant is moderator', async () => {
|
||||
await updateRecordingAccessPreferencesInRoom(
|
||||
roomData.room.roomId,
|
||||
MeetRecordingAccess.ADMIN_MODERATOR_PUBLISHER
|
||||
MeetRecordingAccess.ADMIN_MODERATOR_SPEAKER
|
||||
);
|
||||
const recordingCookie = await generateRecordingTokenCookie(
|
||||
roomData.room.roomId,
|
||||
@ -559,11 +559,11 @@ describe('Recording API Security Tests', () => {
|
||||
expect(response.status).toBe(200);
|
||||
});
|
||||
|
||||
it('should fail when recording access is admin-moderator and participant is publisher', async () => {
|
||||
it('should fail when recording access is admin-moderator and participant is speaker', async () => {
|
||||
await updateRecordingAccessPreferencesInRoom(roomData.room.roomId, MeetRecordingAccess.ADMIN_MODERATOR);
|
||||
const recordingCookie = await generateRecordingTokenCookie(
|
||||
roomData.room.roomId,
|
||||
roomData.publisherSecret
|
||||
roomData.speakerSecret
|
||||
);
|
||||
|
||||
const response = await request(app)
|
||||
@ -651,14 +651,14 @@ describe('Recording API Security Tests', () => {
|
||||
expect(response.status).toBe(200);
|
||||
});
|
||||
|
||||
it('should succeed when recording access is admin-moderator-publisher and participant is publisher', async () => {
|
||||
it('should succeed when recording access is admin-moderator-speaker and participant is speaker', async () => {
|
||||
await updateRecordingAccessPreferencesInRoom(
|
||||
roomData.room.roomId,
|
||||
MeetRecordingAccess.ADMIN_MODERATOR_PUBLISHER
|
||||
MeetRecordingAccess.ADMIN_MODERATOR_SPEAKER
|
||||
);
|
||||
const recordingCookie = await generateRecordingTokenCookie(
|
||||
roomData.room.roomId,
|
||||
roomData.publisherSecret
|
||||
roomData.speakerSecret
|
||||
);
|
||||
|
||||
const response = await request(app)
|
||||
@ -667,10 +667,10 @@ describe('Recording API Security Tests', () => {
|
||||
expect(response.status).toBe(200);
|
||||
});
|
||||
|
||||
it('should succeed when recording access is admin-moderator-publisher and participant is moderator', async () => {
|
||||
it('should succeed when recording access is admin-moderator-speaker and participant is moderator', async () => {
|
||||
await updateRecordingAccessPreferencesInRoom(
|
||||
roomData.room.roomId,
|
||||
MeetRecordingAccess.ADMIN_MODERATOR_PUBLISHER
|
||||
MeetRecordingAccess.ADMIN_MODERATOR_SPEAKER
|
||||
);
|
||||
const recordingCookie = await generateRecordingTokenCookie(
|
||||
roomData.room.roomId,
|
||||
@ -683,11 +683,11 @@ describe('Recording API Security Tests', () => {
|
||||
expect(response.status).toBe(200);
|
||||
});
|
||||
|
||||
it('should fail when recording access is admin-moderator and participant is publisher', async () => {
|
||||
it('should fail when recording access is admin-moderator and participant is speaker', async () => {
|
||||
await updateRecordingAccessPreferencesInRoom(roomData.room.roomId, MeetRecordingAccess.ADMIN_MODERATOR);
|
||||
const recordingCookie = await generateRecordingTokenCookie(
|
||||
roomData.room.roomId,
|
||||
roomData.publisherSecret
|
||||
roomData.speakerSecret
|
||||
);
|
||||
|
||||
const response = await request(app)
|
||||
@ -727,14 +727,14 @@ describe('Recording API Security Tests', () => {
|
||||
expect(response.status).toBe(200);
|
||||
});
|
||||
|
||||
it('should succeed when recording access is admin-moderator-publisher and participant is publisher', async () => {
|
||||
it('should succeed when recording access is admin-moderator-speaker and participant is speaker', async () => {
|
||||
await updateRecordingAccessPreferencesInRoom(
|
||||
roomData.room.roomId,
|
||||
MeetRecordingAccess.ADMIN_MODERATOR_PUBLISHER
|
||||
MeetRecordingAccess.ADMIN_MODERATOR_SPEAKER
|
||||
);
|
||||
const recordingCookie = await generateRecordingTokenCookie(
|
||||
roomData.room.roomId,
|
||||
roomData.publisherSecret
|
||||
roomData.speakerSecret
|
||||
);
|
||||
|
||||
const response = await request(app)
|
||||
@ -744,10 +744,10 @@ describe('Recording API Security Tests', () => {
|
||||
expect(response.status).toBe(200);
|
||||
});
|
||||
|
||||
it('should succeed when recording access is admin-moderator-publisher and participant is moderator', async () => {
|
||||
it('should succeed when recording access is admin-moderator-speaker and participant is moderator', async () => {
|
||||
await updateRecordingAccessPreferencesInRoom(
|
||||
roomData.room.roomId,
|
||||
MeetRecordingAccess.ADMIN_MODERATOR_PUBLISHER
|
||||
MeetRecordingAccess.ADMIN_MODERATOR_SPEAKER
|
||||
);
|
||||
const recordingCookie = await generateRecordingTokenCookie(
|
||||
roomData.room.roomId,
|
||||
@ -761,11 +761,11 @@ describe('Recording API Security Tests', () => {
|
||||
expect(response.status).toBe(200);
|
||||
});
|
||||
|
||||
it('should fail when recording access is admin-moderator and participant is publisher', async () => {
|
||||
it('should fail when recording access is admin-moderator and participant is speaker', async () => {
|
||||
await updateRecordingAccessPreferencesInRoom(roomData.room.roomId, MeetRecordingAccess.ADMIN_MODERATOR);
|
||||
const recordingCookie = await generateRecordingTokenCookie(
|
||||
roomData.room.roomId,
|
||||
roomData.publisherSecret
|
||||
roomData.speakerSecret
|
||||
);
|
||||
|
||||
const response = await request(app)
|
||||
|
||||
@ -142,11 +142,11 @@ describe('Room API Security Tests', () => {
|
||||
expect(response.status).toBe(403);
|
||||
});
|
||||
|
||||
it('should succeed when participant is publisher', async () => {
|
||||
it('should succeed when participant is speaker', async () => {
|
||||
const response = await request(app)
|
||||
.get(`${ROOMS_PATH}/${roomData.room.roomId}`)
|
||||
.set('Cookie', roomData.publisherCookie)
|
||||
.set(INTERNAL_CONFIG.PARTICIPANT_ROLE_HEADER, ParticipantRole.PUBLISHER);
|
||||
.set('Cookie', roomData.speakerCookie)
|
||||
.set(INTERNAL_CONFIG.PARTICIPANT_ROLE_HEADER, ParticipantRole.SPEAKER);
|
||||
expect(response.status).toBe(200);
|
||||
});
|
||||
});
|
||||
@ -181,7 +181,7 @@ describe('Room API Security Tests', () => {
|
||||
const roomPreferences = {
|
||||
recordingPreferences: {
|
||||
enabled: false,
|
||||
allowAccessTo: MeetRecordingAccess.ADMIN_MODERATOR_PUBLISHER
|
||||
allowAccessTo: MeetRecordingAccess.ADMIN_MODERATOR_SPEAKER
|
||||
},
|
||||
chatPreferences: { enabled: true },
|
||||
virtualBackgroundPreferences: { enabled: true }
|
||||
@ -260,21 +260,21 @@ describe('Room API Security Tests', () => {
|
||||
expect(response.status).toBe(403);
|
||||
});
|
||||
|
||||
it('should succeed when participant is publisher', async () => {
|
||||
it('should succeed when participant is speaker', async () => {
|
||||
const response = await request(app)
|
||||
.get(`${INTERNAL_ROOMS_PATH}/${roomData.room.roomId}/preferences`)
|
||||
.set('Cookie', roomData.publisherCookie)
|
||||
.set(INTERNAL_CONFIG.PARTICIPANT_ROLE_HEADER, ParticipantRole.PUBLISHER);
|
||||
.set('Cookie', roomData.speakerCookie)
|
||||
.set(INTERNAL_CONFIG.PARTICIPANT_ROLE_HEADER, ParticipantRole.SPEAKER);
|
||||
expect(response.status).toBe(200);
|
||||
});
|
||||
|
||||
it('should fail when participant is publisher of a different room', async () => {
|
||||
it('should fail when participant is speaker of a different room', async () => {
|
||||
const newRoomData = await setupSingleRoom();
|
||||
|
||||
const response = await request(app)
|
||||
.get(`${INTERNAL_ROOMS_PATH}/${roomData.room.roomId}/preferences`)
|
||||
.set('Cookie', newRoomData.publisherCookie)
|
||||
.set(INTERNAL_CONFIG.PARTICIPANT_ROLE_HEADER, ParticipantRole.PUBLISHER);
|
||||
.set('Cookie', newRoomData.speakerCookie)
|
||||
.set(INTERNAL_CONFIG.PARTICIPANT_ROLE_HEADER, ParticipantRole.SPEAKER);
|
||||
expect(response.status).toBe(403);
|
||||
});
|
||||
});
|
||||
@ -289,16 +289,16 @@ describe('Room API Security Tests', () => {
|
||||
beforeEach(async () => {
|
||||
await updateRecordingAccessPreferencesInRoom(
|
||||
roomData.room.roomId,
|
||||
MeetRecordingAccess.ADMIN_MODERATOR_PUBLISHER
|
||||
MeetRecordingAccess.ADMIN_MODERATOR_SPEAKER
|
||||
);
|
||||
});
|
||||
|
||||
it('should succeed when no authentication is required and participant is publisher', async () => {
|
||||
it('should succeed when no authentication is required and participant is speaker', async () => {
|
||||
await changeSecurityPreferences(AuthMode.NONE);
|
||||
|
||||
const response = await request(app)
|
||||
.post(`${INTERNAL_ROOMS_PATH}/${roomData.room.roomId}/recording-token`)
|
||||
.send({ secret: roomData.publisherSecret });
|
||||
.send({ secret: roomData.speakerSecret });
|
||||
expect(response.status).toBe(200);
|
||||
});
|
||||
|
||||
@ -311,12 +311,12 @@ describe('Room API Security Tests', () => {
|
||||
expect(response.status).toBe(200);
|
||||
});
|
||||
|
||||
it('should succeed when authentication is required for moderator and participant is publisher', async () => {
|
||||
it('should succeed when authentication is required for moderator and participant is speaker', async () => {
|
||||
await changeSecurityPreferences(AuthMode.MODERATORS_ONLY);
|
||||
|
||||
const response = await request(app)
|
||||
.post(`${INTERNAL_ROOMS_PATH}/${roomData.room.roomId}/recording-token`)
|
||||
.send({ secret: roomData.publisherSecret });
|
||||
.send({ secret: roomData.speakerSecret });
|
||||
expect(response.status).toBe(200);
|
||||
});
|
||||
|
||||
@ -339,22 +339,22 @@ describe('Room API Security Tests', () => {
|
||||
expect(response.status).toBe(401);
|
||||
});
|
||||
|
||||
it('should succeed when authentication is required for all users, participant is publisher and authenticated', async () => {
|
||||
it('should succeed when authentication is required for all users, participant is speaker and authenticated', async () => {
|
||||
await changeSecurityPreferences(AuthMode.ALL_USERS);
|
||||
|
||||
const response = await request(app)
|
||||
.post(`${INTERNAL_ROOMS_PATH}/${roomData.room.roomId}/recording-token`)
|
||||
.set('Cookie', adminCookie)
|
||||
.send({ secret: roomData.publisherSecret });
|
||||
.send({ secret: roomData.speakerSecret });
|
||||
expect(response.status).toBe(200);
|
||||
});
|
||||
|
||||
it('should fail when authentication is required for all users and participant is publisher but not authenticated', async () => {
|
||||
it('should fail when authentication is required for all users and participant is speaker but not authenticated', async () => {
|
||||
await changeSecurityPreferences(AuthMode.ALL_USERS);
|
||||
|
||||
const response = await request(app)
|
||||
.post(`${INTERNAL_ROOMS_PATH}/${roomData.room.roomId}/recording-token`)
|
||||
.send({ secret: roomData.publisherSecret });
|
||||
.send({ secret: roomData.speakerSecret });
|
||||
expect(response.status).toBe(401);
|
||||
});
|
||||
|
||||
|
||||
@ -11,12 +11,7 @@
|
||||
<div class="toolbar-left" id="toolbar-left">
|
||||
<mat-form-field class="search-field" appearance="outline" id="search-field">
|
||||
<mat-label>Search rooms</mat-label>
|
||||
<input
|
||||
matInput
|
||||
[formControl]="nameFilterControl"
|
||||
placeholder="Search by room name"
|
||||
id="search-input"
|
||||
/>
|
||||
<input matInput [formControl]="nameFilterControl" placeholder="Search by room name" id="search-input" />
|
||||
<mat-icon matSuffix>search</mat-icon>
|
||||
</mat-form-field>
|
||||
</div>
|
||||
@ -285,11 +280,11 @@
|
||||
</button>
|
||||
<button
|
||||
mat-menu-item
|
||||
(click)="copyPublisherLink(room)"
|
||||
id="copy-publisher-link-btn-{{ room.roomId }}"
|
||||
(click)="copySpeakerLink(room)"
|
||||
id="copy-speaker-link-btn-{{ room.roomId }}"
|
||||
>
|
||||
<mat-icon>content_copy</mat-icon>
|
||||
<span>Copy Publisher Link</span>
|
||||
<span>Copy Speaker Link</span>
|
||||
</button>
|
||||
|
||||
<mat-divider id="actions-divider-{{ room.roomId }}"></mat-divider>
|
||||
|
||||
@ -6,217 +6,213 @@ import { RoomsListsComponent } from './rooms-lists.component';
|
||||
import { MeetRoom } from '../../typings/ce';
|
||||
|
||||
describe('RoomsListsComponent', () => {
|
||||
let component: RoomsListsComponent;
|
||||
let fixture: ComponentFixture<RoomsListsComponent>;
|
||||
let component: RoomsListsComponent;
|
||||
let fixture: ComponentFixture<RoomsListsComponent>;
|
||||
|
||||
const mockRooms: MeetRoom[] = [
|
||||
{
|
||||
roomId: 'test-room-1',
|
||||
creationDate: 1642248000000, // 2024-01-15T10:00:00Z
|
||||
markedForDeletion: false,
|
||||
autoDeletionDate: undefined,
|
||||
roomIdPrefix: 'test',
|
||||
moderatorRoomUrl: 'http://localhost/room/test-room-1?secret=mod-123',
|
||||
publisherRoomUrl: 'http://localhost/room/test-room-1?secret=pub-123',
|
||||
preferences: {
|
||||
chatPreferences: { enabled: true },
|
||||
recordingPreferences: { enabled: false },
|
||||
virtualBackgroundPreferences: { enabled: true }
|
||||
}
|
||||
},
|
||||
{
|
||||
roomId: 'test-room-2',
|
||||
creationDate: 1642334400000, // 2024-01-16T14:30:00Z
|
||||
markedForDeletion: true,
|
||||
autoDeletionDate: 1643673600000, // 2024-02-01T00:00:00Z
|
||||
roomIdPrefix: 'test',
|
||||
moderatorRoomUrl: 'http://localhost/room/test-room-2?secret=mod-456',
|
||||
publisherRoomUrl: 'http://localhost/room/test-room-2?secret=pub-456',
|
||||
preferences: {
|
||||
chatPreferences: { enabled: true },
|
||||
recordingPreferences: { enabled: false },
|
||||
virtualBackgroundPreferences: { enabled: true }
|
||||
}
|
||||
}
|
||||
];
|
||||
const mockRooms: MeetRoom[] = [
|
||||
{
|
||||
roomId: 'test-room-1',
|
||||
creationDate: 1642248000000, // 2024-01-15T10:00:00Z
|
||||
markedForDeletion: false,
|
||||
autoDeletionDate: undefined,
|
||||
roomIdPrefix: 'test',
|
||||
moderatorRoomUrl: 'http://localhost/room/test-room-1?secret=mod-123',
|
||||
speakerRoomUrl: 'http://localhost/room/test-room-1?secret=pub-123',
|
||||
preferences: {
|
||||
chatPreferences: { enabled: true },
|
||||
recordingPreferences: { enabled: false },
|
||||
virtualBackgroundPreferences: { enabled: true }
|
||||
}
|
||||
},
|
||||
{
|
||||
roomId: 'test-room-2',
|
||||
creationDate: 1642334400000, // 2024-01-16T14:30:00Z
|
||||
markedForDeletion: true,
|
||||
autoDeletionDate: 1643673600000, // 2024-02-01T00:00:00Z
|
||||
roomIdPrefix: 'test',
|
||||
moderatorRoomUrl: 'http://localhost/room/test-room-2?secret=mod-456',
|
||||
speakerRoomUrl: 'http://localhost/room/test-room-2?secret=pub-456',
|
||||
preferences: {
|
||||
chatPreferences: { enabled: true },
|
||||
recordingPreferences: { enabled: false },
|
||||
virtualBackgroundPreferences: { enabled: true }
|
||||
}
|
||||
}
|
||||
];
|
||||
|
||||
beforeEach(async () => {
|
||||
await TestBed.configureTestingModule({
|
||||
imports: [
|
||||
RoomsListsComponent,
|
||||
NoopAnimationsModule,
|
||||
MatSnackBarModule
|
||||
]
|
||||
}).compileComponents();
|
||||
beforeEach(async () => {
|
||||
await TestBed.configureTestingModule({
|
||||
imports: [RoomsListsComponent, NoopAnimationsModule, MatSnackBarModule]
|
||||
}).compileComponents();
|
||||
|
||||
fixture = TestBed.createComponent(RoomsListsComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture = TestBed.createComponent(RoomsListsComponent);
|
||||
component = fixture.componentInstance;
|
||||
|
||||
// Set up test data
|
||||
component.rooms = mockRooms;
|
||||
component.loading = false;
|
||||
component.canDeleteRooms = true;
|
||||
// Set up test data
|
||||
component.rooms = mockRooms;
|
||||
component.loading = false;
|
||||
component.canDeleteRooms = true;
|
||||
|
||||
fixture.detectChanges();
|
||||
});
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
|
||||
it('should initialize with correct default values', () => {
|
||||
expect(component.rooms).toEqual(mockRooms);
|
||||
expect(component.canDeleteRooms).toBe(true);
|
||||
expect(component.loading).toBe(false);
|
||||
expect(component.showFilters).toBe(false);
|
||||
expect(component.showSelection).toBe(true);
|
||||
expect(component.emptyMessage).toBe('No rooms found');
|
||||
});
|
||||
it('should initialize with correct default values', () => {
|
||||
expect(component.rooms).toEqual(mockRooms);
|
||||
expect(component.canDeleteRooms).toBe(true);
|
||||
expect(component.loading).toBe(false);
|
||||
expect(component.showFilters).toBe(false);
|
||||
expect(component.showSelection).toBe(true);
|
||||
expect(component.emptyMessage).toBe('No rooms found');
|
||||
});
|
||||
|
||||
it('should update displayed columns based on showSelection', () => {
|
||||
component.showSelection = false;
|
||||
component.ngOnInit();
|
||||
expect(component.displayedColumns).not.toContain('select');
|
||||
it('should update displayed columns based on showSelection', () => {
|
||||
component.showSelection = false;
|
||||
component.ngOnInit();
|
||||
expect(component.displayedColumns).not.toContain('select');
|
||||
|
||||
component.showSelection = true;
|
||||
component.ngOnInit();
|
||||
expect(component.displayedColumns).toContain('select');
|
||||
});
|
||||
component.showSelection = true;
|
||||
component.ngOnInit();
|
||||
expect(component.displayedColumns).toContain('select');
|
||||
});
|
||||
|
||||
it('should determine room status correctly', () => {
|
||||
expect(component.isRoomActive(mockRooms[0])).toBe(true);
|
||||
expect(component.isRoomActive(mockRooms[1])).toBe(false);
|
||||
expect(component.isRoomInactive(mockRooms[0])).toBe(false);
|
||||
expect(component.isRoomInactive(mockRooms[1])).toBe(true);
|
||||
});
|
||||
it('should determine room status correctly', () => {
|
||||
expect(component.isRoomActive(mockRooms[0])).toBe(true);
|
||||
expect(component.isRoomActive(mockRooms[1])).toBe(false);
|
||||
expect(component.isRoomInactive(mockRooms[0])).toBe(false);
|
||||
expect(component.isRoomInactive(mockRooms[1])).toBe(true);
|
||||
});
|
||||
|
||||
it('should return correct status information', () => {
|
||||
expect(component.getRoomStatus(mockRooms[0])).toBe('Active');
|
||||
expect(component.getRoomStatus(mockRooms[1])).toBe('Inactive');
|
||||
it('should return correct status information', () => {
|
||||
expect(component.getRoomStatus(mockRooms[0])).toBe('Active');
|
||||
expect(component.getRoomStatus(mockRooms[1])).toBe('Inactive');
|
||||
|
||||
expect(component.getStatusIcon(mockRooms[0])).toBe('check_circle');
|
||||
expect(component.getStatusIcon(mockRooms[1])).toBe('delete_outline');
|
||||
expect(component.getStatusIcon(mockRooms[0])).toBe('check_circle');
|
||||
expect(component.getStatusIcon(mockRooms[1])).toBe('delete_outline');
|
||||
|
||||
expect(component.getStatusColor(mockRooms[0])).toBe('var(--ov-meet-color-success)');
|
||||
expect(component.getStatusColor(mockRooms[1])).toBe('var(--ov-meet-color-error)');
|
||||
});
|
||||
expect(component.getStatusColor(mockRooms[0])).toBe('var(--ov-meet-color-success)');
|
||||
expect(component.getStatusColor(mockRooms[1])).toBe('var(--ov-meet-color-error)');
|
||||
});
|
||||
|
||||
it('should handle auto-deletion information correctly', () => {
|
||||
expect(component.hasAutoDeletion(mockRooms[0])).toBe(false);
|
||||
expect(component.hasAutoDeletion(mockRooms[1])).toBe(true);
|
||||
it('should handle auto-deletion information correctly', () => {
|
||||
expect(component.hasAutoDeletion(mockRooms[0])).toBe(false);
|
||||
expect(component.hasAutoDeletion(mockRooms[1])).toBe(true);
|
||||
|
||||
expect(component.getAutoDeletionStatus(mockRooms[0])).toBe('Not scheduled');
|
||||
expect(component.getAutoDeletionStatus(mockRooms[1])).toBe('Scheduled');
|
||||
expect(component.getAutoDeletionStatus(mockRooms[0])).toBe('Not scheduled');
|
||||
expect(component.getAutoDeletionStatus(mockRooms[1])).toBe('Scheduled');
|
||||
|
||||
expect(component.getAutoDeletionIcon(mockRooms[0])).toBe('close');
|
||||
expect(component.getAutoDeletionIcon(mockRooms[1])).toBe('auto_delete');
|
||||
});
|
||||
expect(component.getAutoDeletionIcon(mockRooms[0])).toBe('close');
|
||||
expect(component.getAutoDeletionIcon(mockRooms[1])).toBe('auto_delete');
|
||||
});
|
||||
|
||||
it('should handle room selection correctly', () => {
|
||||
const room = mockRooms[0];
|
||||
it('should handle room selection correctly', () => {
|
||||
const room = mockRooms[0];
|
||||
|
||||
expect(component.isRoomSelected(room)).toBe(false);
|
||||
expect(component.isRoomSelected(room)).toBe(false);
|
||||
|
||||
component.toggleRoomSelection(room);
|
||||
expect(component.isRoomSelected(room)).toBe(true);
|
||||
component.toggleRoomSelection(room);
|
||||
expect(component.isRoomSelected(room)).toBe(true);
|
||||
|
||||
component.toggleRoomSelection(room);
|
||||
expect(component.isRoomSelected(room)).toBe(false);
|
||||
});
|
||||
component.toggleRoomSelection(room);
|
||||
expect(component.isRoomSelected(room)).toBe(false);
|
||||
});
|
||||
|
||||
it('should determine if room can be selected', () => {
|
||||
expect(component.canSelectRoom(mockRooms[0])).toBe(true); // Active room
|
||||
expect(component.canSelectRoom(mockRooms[1])).toBe(false); // Marked for deletion
|
||||
});
|
||||
it('should determine if room can be selected', () => {
|
||||
expect(component.canSelectRoom(mockRooms[0])).toBe(true); // Active room
|
||||
expect(component.canSelectRoom(mockRooms[1])).toBe(false); // Marked for deletion
|
||||
});
|
||||
|
||||
it('should determine room permissions correctly', () => {
|
||||
expect(component.canOpenRoom(mockRooms[0])).toBe(true);
|
||||
expect(component.canOpenRoom(mockRooms[1])).toBe(false);
|
||||
it('should determine room permissions correctly', () => {
|
||||
expect(component.canOpenRoom(mockRooms[0])).toBe(true);
|
||||
expect(component.canOpenRoom(mockRooms[1])).toBe(false);
|
||||
|
||||
expect(component.canEditRoom(mockRooms[0])).toBe(true);
|
||||
expect(component.canEditRoom(mockRooms[1])).toBe(false);
|
||||
expect(component.canEditRoom(mockRooms[0])).toBe(true);
|
||||
expect(component.canEditRoom(mockRooms[1])).toBe(false);
|
||||
|
||||
expect(component.canDeleteRoom(mockRooms[0])).toBe(true);
|
||||
expect(component.canDeleteRoom(mockRooms[1])).toBe(false);
|
||||
});
|
||||
expect(component.canDeleteRoom(mockRooms[0])).toBe(true);
|
||||
expect(component.canDeleteRoom(mockRooms[1])).toBe(false);
|
||||
});
|
||||
|
||||
it('should emit room actions correctly', () => {
|
||||
spyOn(component.roomAction, 'emit');
|
||||
it('should emit room actions correctly', () => {
|
||||
spyOn(component.roomAction, 'emit');
|
||||
|
||||
component.openRoom(mockRooms[0]);
|
||||
expect(component.roomAction.emit).toHaveBeenCalledWith({
|
||||
rooms: [mockRooms[0]],
|
||||
action: 'open'
|
||||
});
|
||||
component.openRoom(mockRooms[0]);
|
||||
expect(component.roomAction.emit).toHaveBeenCalledWith({
|
||||
rooms: [mockRooms[0]],
|
||||
action: 'open'
|
||||
});
|
||||
|
||||
component.deleteRoom(mockRooms[0]);
|
||||
expect(component.roomAction.emit).toHaveBeenCalledWith({
|
||||
rooms: [mockRooms[0]],
|
||||
action: 'delete'
|
||||
});
|
||||
component.deleteRoom(mockRooms[0]);
|
||||
expect(component.roomAction.emit).toHaveBeenCalledWith({
|
||||
rooms: [mockRooms[0]],
|
||||
action: 'delete'
|
||||
});
|
||||
|
||||
component.viewSettings(mockRooms[0]);
|
||||
expect(component.roomAction.emit).toHaveBeenCalledWith({
|
||||
rooms: [mockRooms[0]],
|
||||
action: 'settings'
|
||||
});
|
||||
});
|
||||
component.viewSettings(mockRooms[0]);
|
||||
expect(component.roomAction.emit).toHaveBeenCalledWith({
|
||||
rooms: [mockRooms[0]],
|
||||
action: 'settings'
|
||||
});
|
||||
});
|
||||
|
||||
it('should handle batch delete correctly', () => {
|
||||
spyOn(component.roomAction, 'emit');
|
||||
it('should handle batch delete correctly', () => {
|
||||
spyOn(component.roomAction, 'emit');
|
||||
|
||||
// Select some rooms
|
||||
component.toggleRoomSelection(mockRooms[0]);
|
||||
component.bulkDeleteSelected();
|
||||
// Select some rooms
|
||||
component.toggleRoomSelection(mockRooms[0]);
|
||||
component.bulkDeleteSelected();
|
||||
|
||||
expect(component.roomAction.emit).toHaveBeenCalledWith({
|
||||
rooms: [mockRooms[0]],
|
||||
action: 'bulkDelete'
|
||||
});
|
||||
});
|
||||
expect(component.roomAction.emit).toHaveBeenCalledWith({
|
||||
rooms: [mockRooms[0]],
|
||||
action: 'bulkDelete'
|
||||
});
|
||||
});
|
||||
|
||||
it('should emit filter changes', () => {
|
||||
spyOn(component.filterChange, 'emit');
|
||||
it('should emit filter changes', () => {
|
||||
spyOn(component.filterChange, 'emit');
|
||||
|
||||
component.nameFilterControl.setValue('test');
|
||||
component.nameFilterControl.setValue('test');
|
||||
|
||||
// Trigger change detection to simulate the valueChanges observable
|
||||
fixture.detectChanges();
|
||||
// Trigger change detection to simulate the valueChanges observable
|
||||
fixture.detectChanges();
|
||||
|
||||
// The filter change should be emitted through the form control subscription
|
||||
expect(component.filterChange.emit).toHaveBeenCalled();
|
||||
});
|
||||
// The filter change should be emitted through the form control subscription
|
||||
expect(component.filterChange.emit).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should show empty state when no rooms', () => {
|
||||
component.rooms = [];
|
||||
fixture.detectChanges();
|
||||
it('should show empty state when no rooms', () => {
|
||||
component.rooms = [];
|
||||
fixture.detectChanges();
|
||||
|
||||
const emptyState = fixture.nativeElement.querySelector('.no-rooms-state');
|
||||
expect(emptyState).toBeTruthy();
|
||||
});
|
||||
const emptyState = fixture.nativeElement.querySelector('.no-rooms-state');
|
||||
expect(emptyState).toBeTruthy();
|
||||
});
|
||||
|
||||
it('should show loading state', () => {
|
||||
component.loading = true;
|
||||
fixture.detectChanges();
|
||||
it('should show loading state', () => {
|
||||
component.loading = true;
|
||||
fixture.detectChanges();
|
||||
|
||||
const loadingContainer = fixture.nativeElement.querySelector('.loading-container');
|
||||
expect(loadingContainer).toBeTruthy();
|
||||
});
|
||||
const loadingContainer = fixture.nativeElement.querySelector('.loading-container');
|
||||
expect(loadingContainer).toBeTruthy();
|
||||
});
|
||||
|
||||
it('should clear selection correctly', () => {
|
||||
// Select a room first
|
||||
component.toggleRoomSelection(mockRooms[0]);
|
||||
expect(component.selectedRooms().size).toBe(1);
|
||||
it('should clear selection correctly', () => {
|
||||
// Select a room first
|
||||
component.toggleRoomSelection(mockRooms[0]);
|
||||
expect(component.selectedRooms().size).toBe(1);
|
||||
|
||||
// Clear selection
|
||||
component.clearSelection();
|
||||
expect(component.selectedRooms().size).toBe(0);
|
||||
});
|
||||
// Clear selection
|
||||
component.clearSelection();
|
||||
expect(component.selectedRooms().size).toBe(0);
|
||||
});
|
||||
|
||||
it('should get selected rooms correctly', () => {
|
||||
component.toggleRoomSelection(mockRooms[0]);
|
||||
const selectedRooms = component.getSelectedRooms();
|
||||
it('should get selected rooms correctly', () => {
|
||||
component.toggleRoomSelection(mockRooms[0]);
|
||||
const selectedRooms = component.getSelectedRooms();
|
||||
|
||||
expect(selectedRooms).toEqual([mockRooms[0]]);
|
||||
});
|
||||
expect(selectedRooms).toEqual([mockRooms[0]]);
|
||||
});
|
||||
});
|
||||
|
||||
@ -34,7 +34,7 @@ export interface RoomTableAction {
|
||||
| 'open'
|
||||
| 'edit'
|
||||
| 'copyModeratorLink'
|
||||
| 'copyPublisherLink'
|
||||
| 'copySpeakerLink'
|
||||
| 'viewRecordings'
|
||||
| 'delete'
|
||||
| 'bulkDelete';
|
||||
@ -242,8 +242,8 @@ export class RoomsListsComponent implements OnInit, OnChanges {
|
||||
this.roomAction.emit({ rooms: [room], action: 'copyModeratorLink' });
|
||||
}
|
||||
|
||||
copyPublisherLink(room: MeetRoom) {
|
||||
this.roomAction.emit({ rooms: [room], action: 'copyPublisherLink' });
|
||||
copySpeakerLink(room: MeetRoom) {
|
||||
this.roomAction.emit({ rooms: [room], action: 'copySpeakerLink' });
|
||||
}
|
||||
|
||||
viewRecordings(room: MeetRoom) {
|
||||
|
||||
@ -2,6 +2,6 @@ import { ParticipantPermissions, ParticipantRole } from '../typings/ce';
|
||||
|
||||
export interface ParticipantTokenInfo {
|
||||
token: string; // The generated participant token
|
||||
role: ParticipantRole; // Role of the participant (e.g., 'moderator', 'publisher')
|
||||
role: ParticipantRole; // Role of the participant (e.g., 'moderator', 'speaker')
|
||||
permissions: ParticipantPermissions; // List of permissions granted to the participant
|
||||
}
|
||||
|
||||
@ -44,7 +44,7 @@ export class RecordingPreferencesComponent implements OnDestroy {
|
||||
title: 'Allow Recording',
|
||||
description:
|
||||
'Enable recording capabilities for this room. Recordings can be started manually or automatically.',
|
||||
icon: 'video_library',
|
||||
icon: 'video_library'
|
||||
// recommended: true
|
||||
},
|
||||
{
|
||||
@ -64,8 +64,8 @@ export class RecordingPreferencesComponent implements OnDestroy {
|
||||
label: 'Admin and Moderators'
|
||||
},
|
||||
{
|
||||
value: MeetRecordingAccess.ADMIN_MODERATOR_PUBLISHER,
|
||||
label: 'Admin, Moderators and Publishers'
|
||||
value: MeetRecordingAccess.ADMIN_MODERATOR_SPEAKER,
|
||||
label: 'Admin, Moderators and Speakers'
|
||||
}
|
||||
];
|
||||
|
||||
|
||||
@ -86,8 +86,8 @@ export class RoomsComponent implements OnInit {
|
||||
case 'copyModeratorLink':
|
||||
this.copyModeratorLink(action.rooms[0]);
|
||||
break;
|
||||
case 'copyPublisherLink':
|
||||
this.copyPublisherLink(action.rooms[0]);
|
||||
case 'copySpeakerLink':
|
||||
this.copySpeakerLink(action.rooms[0]);
|
||||
break;
|
||||
case 'viewRecordings':
|
||||
await this.viewRecordings(action.rooms[0]);
|
||||
@ -236,9 +236,9 @@ export class RoomsComponent implements OnInit {
|
||||
this.notificationService.showSnackbar('Moderator link copied to clipboard');
|
||||
}
|
||||
|
||||
private copyPublisherLink(room: MeetRoom) {
|
||||
this.clipboard.copy(room.publisherRoomUrl);
|
||||
this.notificationService.showSnackbar('Publisher link copied to clipboard');
|
||||
private copySpeakerLink(room: MeetRoom) {
|
||||
this.clipboard.copy(room.speakerRoomUrl);
|
||||
this.notificationService.showSnackbar('Speaker link copied to clipboard');
|
||||
}
|
||||
|
||||
private async viewRecordings(room: MeetRoom) {
|
||||
|
||||
@ -52,7 +52,7 @@ export class ErrorComponent implements OnInit {
|
||||
const reasonMap: { [key in ErrorReason]: { title: string; message: string } } = {
|
||||
[ErrorReason.MISSING_ROOM_SECRET]: {
|
||||
title: 'Missing secret',
|
||||
message: 'You need to provide a secret to join the room as a moderator or publisher'
|
||||
message: 'You need to provide a secret to join the room as a moderator or speaker'
|
||||
},
|
||||
[ErrorReason.MISSING_RECORDING_SECRET]: {
|
||||
title: 'Missing secret',
|
||||
@ -60,7 +60,7 @@ export class ErrorComponent implements OnInit {
|
||||
},
|
||||
[ErrorReason.INVALID_ROOM_SECRET]: {
|
||||
title: 'Invalid secret',
|
||||
message: 'The secret provided to join the room is neither valid for moderators nor publishers'
|
||||
message: 'The secret provided to join the room is neither valid for moderators nor speakers'
|
||||
},
|
||||
[ErrorReason.INVALID_RECORDING_SECRET]: {
|
||||
title: 'Invalid secret',
|
||||
|
||||
@ -44,9 +44,9 @@
|
||||
<div *ovToolbarAdditionalButtons>
|
||||
<!-- Copy Links Button -->
|
||||
<button
|
||||
id="copy-publisher-link"
|
||||
id="copy-speaker-link"
|
||||
mat-icon-button
|
||||
(click)="copyPublisherLink()"
|
||||
(click)="copySpeakerLink()"
|
||||
[disableRipple]="true"
|
||||
matTooltip="Copy the meeting link"
|
||||
>
|
||||
@ -96,7 +96,7 @@
|
||||
<div class="share-meeting-link-container">
|
||||
<ov-share-meeting-link
|
||||
[meetingUrl]="hostname + '/' + roomId"
|
||||
(copyClicked)="copyPublisherLink()"
|
||||
(copyClicked)="copySpeakerLink()"
|
||||
></ov-share-meeting-link>
|
||||
</div>
|
||||
</div>
|
||||
@ -110,7 +110,7 @@
|
||||
[titleSize]="'xl'"
|
||||
[titleWeight]="'bold'"
|
||||
[meetingUrl]="hostname + '/' + roomId"
|
||||
(copyClicked)="copyPublisherLink()"
|
||||
(copyClicked)="copySpeakerLink()"
|
||||
></ov-share-meeting-link>
|
||||
</div>
|
||||
}
|
||||
@ -218,7 +218,7 @@
|
||||
@if (features().canModerateRoom) {
|
||||
<ov-share-meeting-link
|
||||
[meetingUrl]="hostname + '/' + roomId"
|
||||
(copyClicked)="copyPublisherLink()"
|
||||
(copyClicked)="copySpeakerLink()"
|
||||
></ov-share-meeting-link>
|
||||
}
|
||||
|
||||
|
||||
@ -90,7 +90,7 @@ export class MeetingComponent implements OnInit {
|
||||
roomSecret = '';
|
||||
participantName = '';
|
||||
participantToken = '';
|
||||
participantRole: ParticipantRole = ParticipantRole.PUBLISHER;
|
||||
participantRole: ParticipantRole = ParticipantRole.SPEAKER;
|
||||
remoteParticipants: ParticipantModel[] = [];
|
||||
|
||||
showMeeting = false;
|
||||
@ -406,9 +406,9 @@ export class MeetingComponent implements OnInit {
|
||||
this.notificationService.showSnackbar('Moderator link copied to clipboard');
|
||||
}
|
||||
|
||||
async copyPublisherLink() {
|
||||
this.clipboard.copy(this.room!.publisherRoomUrl);
|
||||
this.notificationService.showSnackbar('Publisher link copied to clipboard');
|
||||
async copySpeakerLink() {
|
||||
this.clipboard.copy(this.room!.speakerRoomUrl);
|
||||
this.notificationService.showSnackbar('Speaker link copied to clipboard');
|
||||
}
|
||||
|
||||
async onRecordingStartRequested(event: RecordingStartRequestedEvent) {
|
||||
|
||||
@ -12,7 +12,7 @@ export class ParticipantTokenService {
|
||||
protected readonly PARTICIPANTS_API = `${HttpService.INTERNAL_API_PATH_PREFIX}/participants`;
|
||||
|
||||
protected participantName?: string;
|
||||
protected participantRole: ParticipantRole = ParticipantRole.PUBLISHER;
|
||||
protected participantRole: ParticipantRole = ParticipantRole.SPEAKER;
|
||||
protected currentTokenInfo?: ParticipantTokenInfo;
|
||||
|
||||
protected log;
|
||||
|
||||
@ -7,7 +7,7 @@ import { MeetRecordingAccess, MeetRoomOptions, MeetRoomPreferences } from '@lib/
|
||||
const DEFAULT_PREFERENCES: MeetRoomPreferences = {
|
||||
recordingPreferences: {
|
||||
enabled: true,
|
||||
allowAccessTo: MeetRecordingAccess.ADMIN_MODERATOR_PUBLISHER
|
||||
allowAccessTo: MeetRecordingAccess.ADMIN_MODERATOR_SPEAKER
|
||||
},
|
||||
chatPreferences: { enabled: true },
|
||||
virtualBackgroundPreferences: { enabled: true }
|
||||
|
||||
@ -56,8 +56,8 @@ test.describe('Web Component E2E Tests', () => {
|
||||
expect(joinElements.length).toBe(1);
|
||||
});
|
||||
|
||||
test('should successfully join as publisher and receive JOINED event', async ({ page }) => {
|
||||
await joinRoomAs('publisher', participantName, page);
|
||||
test('should successfully join as speaker and receive JOINED event', async ({ page }) => {
|
||||
await joinRoomAs('speaker', participantName, page);
|
||||
await page.waitForSelector('.event-JOINED');
|
||||
const joinElements = await page.locator('.event-JOINED').all();
|
||||
expect(joinElements.length).toBe(1);
|
||||
|
||||
@ -91,7 +91,7 @@ test.describe('Room Functionality Tests', () => {
|
||||
|
||||
test.describe('Basic Room Features', () => {
|
||||
test('should show the toolbar and media buttons', async ({ page }) => {
|
||||
await joinRoomAs('publisher', participantName, page);
|
||||
await joinRoomAs('speaker', participantName, page);
|
||||
await waitForElementInIframe(page, '#toolbar');
|
||||
|
||||
// Check media buttons are present
|
||||
@ -102,8 +102,8 @@ test.describe('Room Functionality Tests', () => {
|
||||
});
|
||||
|
||||
test('should start a videoconference and display video elements', async ({ page, browser }) => {
|
||||
// First participant (publisher) joins
|
||||
await joinRoomAs('publisher', participantName, page);
|
||||
// First participant (speaker) joins
|
||||
await joinRoomAs('speaker', participantName, page);
|
||||
|
||||
// Check local video element
|
||||
const localVideo = await waitForElementInIframe(page, '.OV_stream.local');
|
||||
@ -132,7 +132,7 @@ test.describe('Room Functionality Tests', () => {
|
||||
|
||||
test.describe('Screen Sharing', () => {
|
||||
test('should be able to share and stop screen sharing', async ({ page }) => {
|
||||
await joinRoomAs('publisher', participantName, page);
|
||||
await joinRoomAs('speaker', participantName, page);
|
||||
|
||||
await waitForElementInIframe(page, '#toolbar');
|
||||
|
||||
@ -169,7 +169,7 @@ test.describe('Room Functionality Tests', () => {
|
||||
|
||||
test.describe('UI Panels and Components', () => {
|
||||
test('should show and interact with chat panel', async ({ page }) => {
|
||||
await joinRoomAs('publisher', participantName, page);
|
||||
await joinRoomAs('speaker', participantName, page);
|
||||
|
||||
// Open chat panel
|
||||
await waitForElementInIframe(page, '#chat-panel-btn');
|
||||
@ -205,7 +205,7 @@ test.describe('Room Functionality Tests', () => {
|
||||
});
|
||||
|
||||
test('should show participants panel', async ({ page }) => {
|
||||
await joinRoomAs('publisher', participantName, page);
|
||||
await joinRoomAs('speaker', participantName, page);
|
||||
|
||||
// Open participants panel
|
||||
await waitForElementInIframe(page, '#participants-panel-btn');
|
||||
@ -219,7 +219,7 @@ test.describe('Room Functionality Tests', () => {
|
||||
});
|
||||
|
||||
test('should show settings panel', async ({ page }) => {
|
||||
await joinRoomAs('publisher', participantName, page);
|
||||
await joinRoomAs('speaker', participantName, page);
|
||||
|
||||
await openMoreOptionsMenu(page);
|
||||
|
||||
@ -240,7 +240,7 @@ test.describe('Room Functionality Tests', () => {
|
||||
|
||||
test.describe('Advanced Features', () => {
|
||||
test('should apply virtual background and detect visual changes', async ({ page }) => {
|
||||
await joinRoomAs('publisher', participantName, page);
|
||||
await joinRoomAs('speaker', participantName, page);
|
||||
|
||||
// Wait for video element to be ready
|
||||
await waitForElementInIframe(page, '.OV_video-element');
|
||||
|
||||
@ -92,7 +92,7 @@ test.describe('Recording Access Tests', () => {
|
||||
await waitForElementInIframe(page, '#view-recordings-btn', { state: 'hidden' });
|
||||
});
|
||||
|
||||
test('should publisher not be able to access recording when access level is set to admin', async ({ page }) => {
|
||||
test('should speaker not be able to access recording when access level is set to admin', async ({ page }) => {
|
||||
await updateRoomPreferences(
|
||||
roomId,
|
||||
{
|
||||
@ -108,7 +108,7 @@ test.describe('Recording Access Tests', () => {
|
||||
|
||||
await page.goto(MEET_TESTAPP_URL);
|
||||
await prepareForJoiningRoom(page, MEET_TESTAPP_URL, roomId);
|
||||
await accessRoomAs('publisher', page);
|
||||
await accessRoomAs('speaker', page);
|
||||
|
||||
await waitForElementInIframe(page, '#view-recordings-btn', { state: 'hidden' });
|
||||
});
|
||||
@ -134,7 +134,7 @@ test.describe('Recording Access Tests', () => {
|
||||
await waitForElementInIframe(page, 'app-room-recordings', { state: 'visible' });
|
||||
});
|
||||
|
||||
test('should publisher not be able to access recording when access level is set to moderator', async ({ page }) => {
|
||||
test('should speaker not be able to access recording when access level is set to moderator', async ({ page }) => {
|
||||
await updateRoomPreferences(
|
||||
roomId,
|
||||
{
|
||||
@ -150,12 +150,12 @@ test.describe('Recording Access Tests', () => {
|
||||
|
||||
await page.goto(MEET_TESTAPP_URL);
|
||||
await prepareForJoiningRoom(page, MEET_TESTAPP_URL, roomId);
|
||||
await accessRoomAs('publisher', page);
|
||||
await accessRoomAs('speaker', page);
|
||||
|
||||
await waitForElementInIframe(page, '#view-recordings-btn', { state: 'hidden' });
|
||||
});
|
||||
|
||||
test('should allow moderators to access recording when access level is set to publisher', async ({ page }) => {
|
||||
test('should allow moderators to access recording when access level is set to speaker', async ({ page }) => {
|
||||
await updateRoomPreferences(
|
||||
roomId,
|
||||
{
|
||||
@ -176,14 +176,14 @@ test.describe('Recording Access Tests', () => {
|
||||
await waitForElementInIframe(page, 'app-room-recordings', { state: 'visible' });
|
||||
});
|
||||
|
||||
test('should allow publisher to access recording when access level is set to publisher', async ({ page }) => {
|
||||
test('should allow speaker to access recording when access level is set to speaker', async ({ page }) => {
|
||||
await updateRoomPreferences(
|
||||
roomId,
|
||||
{
|
||||
chatPreferences: { enabled: true },
|
||||
recordingPreferences: {
|
||||
enabled: true,
|
||||
allowAccessTo: MeetRecordingAccess.ADMIN_MODERATOR_PUBLISHER
|
||||
allowAccessTo: MeetRecordingAccess.ADMIN_MODERATOR_SPEAKER
|
||||
},
|
||||
virtualBackgroundPreferences: { enabled: true }
|
||||
},
|
||||
@ -192,7 +192,7 @@ test.describe('Recording Access Tests', () => {
|
||||
|
||||
await page.goto(MEET_TESTAPP_URL);
|
||||
await prepareForJoiningRoom(page, MEET_TESTAPP_URL, roomId);
|
||||
await viewRecordingsAs('publisher', page);
|
||||
await viewRecordingsAs('speaker', page);
|
||||
|
||||
await waitForElementInIframe(page, 'app-room-recordings', { state: 'visible' });
|
||||
});
|
||||
|
||||
@ -79,7 +79,7 @@ test.describe('UI Feature Preferences Tests', () => {
|
||||
chatPreferences: { enabled: true },
|
||||
recordingPreferences: {
|
||||
enabled: true,
|
||||
allowAccessTo: MeetRecordingAccess.ADMIN_MODERATOR_PUBLISHER
|
||||
allowAccessTo: MeetRecordingAccess.ADMIN_MODERATOR_SPEAKER
|
||||
},
|
||||
virtualBackgroundPreferences: { enabled: true }
|
||||
},
|
||||
@ -88,7 +88,7 @@ test.describe('UI Feature Preferences Tests', () => {
|
||||
|
||||
await page.goto(MEET_TESTAPP_URL);
|
||||
await prepareForJoiningRoom(page, MEET_TESTAPP_URL, roomId);
|
||||
await joinRoomAs('publisher', participantName, page);
|
||||
await joinRoomAs('speaker', participantName, page);
|
||||
|
||||
// Check that chat button is visible
|
||||
const chatButton = await waitForElementInIframe(page, '#chat-panel-btn', { state: 'visible' });
|
||||
@ -103,7 +103,7 @@ test.describe('UI Feature Preferences Tests', () => {
|
||||
chatPreferences: { enabled: false },
|
||||
recordingPreferences: {
|
||||
enabled: true,
|
||||
allowAccessTo: MeetRecordingAccess.ADMIN_MODERATOR_PUBLISHER
|
||||
allowAccessTo: MeetRecordingAccess.ADMIN_MODERATOR_SPEAKER
|
||||
},
|
||||
virtualBackgroundPreferences: { enabled: true }
|
||||
},
|
||||
@ -112,7 +112,7 @@ test.describe('UI Feature Preferences Tests', () => {
|
||||
|
||||
await page.goto(MEET_TESTAPP_URL);
|
||||
await prepareForJoiningRoom(page, MEET_TESTAPP_URL, roomId);
|
||||
await joinRoomAs('publisher', participantName, page);
|
||||
await joinRoomAs('speaker', participantName, page);
|
||||
|
||||
// Check that chat button is not visible
|
||||
const chatButton = await waitForElementInIframe(page, '#chat-panel-btn', { state: 'hidden' });
|
||||
@ -132,7 +132,7 @@ test.describe('UI Feature Preferences Tests', () => {
|
||||
chatPreferences: { enabled: true },
|
||||
recordingPreferences: {
|
||||
enabled: true,
|
||||
allowAccessTo: MeetRecordingAccess.ADMIN_MODERATOR_PUBLISHER
|
||||
allowAccessTo: MeetRecordingAccess.ADMIN_MODERATOR_SPEAKER
|
||||
},
|
||||
virtualBackgroundPreferences: { enabled: true }
|
||||
},
|
||||
@ -159,14 +159,14 @@ test.describe('UI Feature Preferences Tests', () => {
|
||||
await leaveRoom(page, 'moderator');
|
||||
});
|
||||
|
||||
test('should not show recording button for publisher', async ({ page }) => {
|
||||
test('should not show recording button for speaker', async ({ page }) => {
|
||||
await updateRoomPreferences(
|
||||
roomId,
|
||||
{
|
||||
chatPreferences: { enabled: true },
|
||||
recordingPreferences: {
|
||||
enabled: true,
|
||||
allowAccessTo: MeetRecordingAccess.ADMIN_MODERATOR_PUBLISHER
|
||||
allowAccessTo: MeetRecordingAccess.ADMIN_MODERATOR_SPEAKER
|
||||
},
|
||||
virtualBackgroundPreferences: { enabled: true }
|
||||
},
|
||||
@ -175,9 +175,9 @@ test.describe('UI Feature Preferences Tests', () => {
|
||||
|
||||
await page.goto(MEET_TESTAPP_URL);
|
||||
await prepareForJoiningRoom(page, MEET_TESTAPP_URL, roomId);
|
||||
await joinRoomAs('publisher', participantName, page);
|
||||
await joinRoomAs('speaker', participantName, page);
|
||||
|
||||
// Check that recording button is not visible for publisher
|
||||
// Check that recording button is not visible for speaker
|
||||
const recordingButton = await waitForElementInIframe(page, '#recording-btn', { state: 'hidden' });
|
||||
await expect(recordingButton).toBeHidden();
|
||||
await leaveRoom(page);
|
||||
@ -191,7 +191,7 @@ test.describe('UI Feature Preferences Tests', () => {
|
||||
chatPreferences: { enabled: true },
|
||||
recordingPreferences: {
|
||||
enabled: false,
|
||||
allowAccessTo: MeetRecordingAccess.ADMIN_MODERATOR_PUBLISHER
|
||||
allowAccessTo: MeetRecordingAccess.ADMIN_MODERATOR_SPEAKER
|
||||
},
|
||||
virtualBackgroundPreferences: { enabled: true }
|
||||
},
|
||||
@ -232,7 +232,7 @@ test.describe('UI Feature Preferences Tests', () => {
|
||||
chatPreferences: { enabled: true },
|
||||
recordingPreferences: {
|
||||
enabled: true,
|
||||
allowAccessTo: MeetRecordingAccess.ADMIN_MODERATOR_PUBLISHER
|
||||
allowAccessTo: MeetRecordingAccess.ADMIN_MODERATOR_SPEAKER
|
||||
},
|
||||
virtualBackgroundPreferences: { enabled: true }
|
||||
},
|
||||
@ -241,7 +241,7 @@ test.describe('UI Feature Preferences Tests', () => {
|
||||
|
||||
await page.goto(MEET_TESTAPP_URL);
|
||||
await prepareForJoiningRoom(page, MEET_TESTAPP_URL, roomId);
|
||||
await joinRoomAs('publisher', participantName, page);
|
||||
await joinRoomAs('speaker', participantName, page);
|
||||
|
||||
// Click more options to reveal virtual background button
|
||||
await openMoreOptionsMenu(page);
|
||||
@ -261,7 +261,7 @@ test.describe('UI Feature Preferences Tests', () => {
|
||||
chatPreferences: { enabled: true },
|
||||
recordingPreferences: {
|
||||
enabled: true,
|
||||
allowAccessTo: MeetRecordingAccess.ADMIN_MODERATOR_PUBLISHER
|
||||
allowAccessTo: MeetRecordingAccess.ADMIN_MODERATOR_SPEAKER
|
||||
},
|
||||
virtualBackgroundPreferences: { enabled: false }
|
||||
},
|
||||
@ -270,7 +270,7 @@ test.describe('UI Feature Preferences Tests', () => {
|
||||
|
||||
await page.goto(MEET_TESTAPP_URL);
|
||||
await prepareForJoiningRoom(page, MEET_TESTAPP_URL, roomId);
|
||||
await joinRoomAs('publisher', participantName, page);
|
||||
await joinRoomAs('speaker', participantName, page);
|
||||
|
||||
// Click more options to reveal virtual background button
|
||||
await openMoreOptionsMenu(page);
|
||||
@ -290,7 +290,7 @@ test.describe('UI Feature Preferences Tests', () => {
|
||||
chatPreferences: { enabled: true },
|
||||
recordingPreferences: {
|
||||
enabled: true,
|
||||
allowAccessTo: MeetRecordingAccess.ADMIN_MODERATOR_PUBLISHER
|
||||
allowAccessTo: MeetRecordingAccess.ADMIN_MODERATOR_SPEAKER
|
||||
},
|
||||
virtualBackgroundPreferences: { enabled: true }
|
||||
},
|
||||
@ -299,7 +299,7 @@ test.describe('UI Feature Preferences Tests', () => {
|
||||
|
||||
await page.goto(MEET_TESTAPP_URL);
|
||||
await prepareForJoiningRoom(page, MEET_TESTAPP_URL, roomId);
|
||||
await joinRoomAs('publisher', participantName, page);
|
||||
await joinRoomAs('speaker', participantName, page);
|
||||
|
||||
await applyVirtualBackground(page, '2');
|
||||
await waitForVirtualBackgroundToApply(page);
|
||||
@ -311,7 +311,7 @@ test.describe('UI Feature Preferences Tests', () => {
|
||||
chatPreferences: { enabled: true },
|
||||
recordingPreferences: {
|
||||
enabled: true,
|
||||
allowAccessTo: MeetRecordingAccess.ADMIN_MODERATOR_PUBLISHER
|
||||
allowAccessTo: MeetRecordingAccess.ADMIN_MODERATOR_SPEAKER
|
||||
},
|
||||
virtualBackgroundPreferences: { enabled: false }
|
||||
},
|
||||
@ -323,7 +323,7 @@ test.describe('UI Feature Preferences Tests', () => {
|
||||
await page.reload();
|
||||
|
||||
await prepareForJoiningRoom(page, MEET_TESTAPP_URL, roomId);
|
||||
await joinRoomAs('publisher', participantName, page);
|
||||
await joinRoomAs('speaker', participantName, page);
|
||||
await page.waitForTimeout(2000);
|
||||
|
||||
const isVBApplied = await isVirtualBackgroundApplied(page);
|
||||
|
||||
@ -92,7 +92,7 @@ export async function interactWithElementInIframe(
|
||||
const getDefaultRoomPreferences = (): MeetRoomPreferences => ({
|
||||
recordingPreferences: {
|
||||
enabled: true,
|
||||
allowAccessTo: MeetRecordingAccess.ADMIN_MODERATOR_PUBLISHER
|
||||
allowAccessTo: MeetRecordingAccess.ADMIN_MODERATOR_SPEAKER
|
||||
},
|
||||
chatPreferences: { enabled: true },
|
||||
virtualBackgroundPreferences: { enabled: true }
|
||||
@ -222,10 +222,10 @@ export const prepareForJoiningRoom = async (page: Page, url: string, roomId: str
|
||||
await page.waitForSelector(`#${roomId}`);
|
||||
await page.click('.dropdown-button');
|
||||
await page.waitForSelector('#join-as-moderator');
|
||||
await page.waitForSelector('#join-as-publisher');
|
||||
await page.waitForSelector('#join-as-speaker');
|
||||
};
|
||||
|
||||
export const joinRoomAs = async (role: 'moderator' | 'publisher', pName: string, page: Page) => {
|
||||
export const joinRoomAs = async (role: 'moderator' | 'speaker', pName: string, page: Page) => {
|
||||
await page.click('#join-as-' + role);
|
||||
const component = page.locator('openvidu-meet');
|
||||
await expect(component).toBeVisible();
|
||||
@ -244,13 +244,13 @@ export const joinRoomAs = async (role: 'moderator' | 'publisher', pName: string,
|
||||
await waitForElementInIframe(page, 'ov-session', { state: 'visible' });
|
||||
};
|
||||
|
||||
export const accessRoomAs = async (role: 'moderator' | 'publisher', page: Page) => {
|
||||
export const accessRoomAs = async (role: 'moderator' | 'speaker', page: Page) => {
|
||||
await page.click('#join-as-' + role);
|
||||
const component = page.locator('openvidu-meet');
|
||||
await expect(component).toBeVisible();
|
||||
};
|
||||
|
||||
export const viewRecordingsAs = async (role: 'moderator' | 'publisher', page: Page) => {
|
||||
export const viewRecordingsAs = async (role: 'moderator' | 'speaker', page: Page) => {
|
||||
await page.click('#join-as-' + role);
|
||||
const component = page.locator('openvidu-meet');
|
||||
await expect(component).toBeVisible();
|
||||
@ -258,7 +258,7 @@ export const viewRecordingsAs = async (role: 'moderator' | 'publisher', page: Pa
|
||||
await interactWithElementInIframe(page, '#view-recordings-btn', { action: 'click' });
|
||||
};
|
||||
|
||||
export const leaveRoom = async (page: Page, role: 'moderator' | 'publisher' = 'publisher') => {
|
||||
export const leaveRoom = async (page: Page, role: 'moderator' | 'speaker' = 'speaker') => {
|
||||
const button = await waitForElementInIframe(page, '#leave-btn');
|
||||
await button.click();
|
||||
|
||||
|
||||
@ -113,9 +113,7 @@ describe('OpenViduMeet Event Handling', () => {
|
||||
// Set attributes
|
||||
const roomUrl = 'https://example.com/room/testRoom-123?secret=123456';
|
||||
component.setAttribute('room-url', roomUrl);
|
||||
component.setAttribute('user', 'testUser');
|
||||
component.setAttribute('role', 'publisher');
|
||||
component.setAttribute('token', 'test-token');
|
||||
component.setAttribute('participant-name', 'testUser');
|
||||
|
||||
// Trigger update
|
||||
(component as any).updateIframeSrc();
|
||||
@ -125,8 +123,6 @@ describe('OpenViduMeet Event Handling', () => {
|
||||
const src = iframe?.src;
|
||||
|
||||
expect(src).toContain(roomUrl);
|
||||
expect(src).toContain('user=testUser');
|
||||
expect(src).toContain('role=publisher');
|
||||
expect(src).toContain('token=test-token');
|
||||
expect(src).toContain('participant-name=testUser');
|
||||
});
|
||||
});
|
||||
|
||||
@ -91,21 +91,21 @@
|
||||
<input
|
||||
type="hidden"
|
||||
name="roomUrl"
|
||||
value="{{ publisherRoomUrl }}"
|
||||
value="{{ speakerRoomUrl }}"
|
||||
/>
|
||||
<input
|
||||
type="hidden"
|
||||
name="participantRole"
|
||||
value="publisher"
|
||||
value="speaker"
|
||||
/>
|
||||
<input type="hidden" name="roomId" value="{{ roomId }}" />
|
||||
|
||||
<button
|
||||
type="submit"
|
||||
id="join-as-publisher"
|
||||
id="join-as-speaker"
|
||||
class="dropdown-item"
|
||||
>
|
||||
Publisher
|
||||
Speaker
|
||||
</button>
|
||||
</form>
|
||||
</li>
|
||||
@ -271,8 +271,8 @@
|
||||
<option value="admin-moderator">
|
||||
Admin & Moderator
|
||||
</option>
|
||||
<option value="admin-moderator-publisher" selected>
|
||||
Admin, Moderator & Publisher
|
||||
<option value="admin-moderator-speaker" selected>
|
||||
Admin, Moderator & Speaker
|
||||
</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
@ -26,7 +26,10 @@ export const getHome = async (_req: Request, res: Response) => {
|
||||
apiUrl: process.env.MEET_API_URL || 'http://localhost:6080/api/v1',
|
||||
apiKey: process.env.MEET_API_KEY || 'meet-api-key'
|
||||
});
|
||||
res.status(500).send('Internal Server Error - Failed to fetch rooms: ' + (error instanceof Error ? error.message : 'Unknown error'));
|
||||
res.status(500).send(
|
||||
'Internal Server Error - Failed to fetch rooms: ' +
|
||||
(error instanceof Error ? error.message : 'Unknown error')
|
||||
);
|
||||
return;
|
||||
}
|
||||
};
|
||||
@ -50,7 +53,10 @@ export const postCreateRoom = async (req: Request, res: Response) => {
|
||||
stack: error instanceof Error ? error.stack : 'No stack trace',
|
||||
requestBody: req.body
|
||||
});
|
||||
res.status(500).send('Internal Server Error - Failed to create room: ' + (error instanceof Error ? error.message : 'Unknown error'));
|
||||
res.status(500).send(
|
||||
'Internal Server Error - Failed to create room: ' +
|
||||
(error instanceof Error ? error.message : 'Unknown error')
|
||||
);
|
||||
return;
|
||||
}
|
||||
};
|
||||
@ -77,7 +83,10 @@ export const deleteRoomCtrl = async (req: Request, res: Response) => {
|
||||
stack: error instanceof Error ? error.stack : 'No stack trace',
|
||||
requestBody: req.body
|
||||
});
|
||||
res.status(500).send('Internal Server Error - Failed to delete room: ' + (error instanceof Error ? error.message : 'Unknown error'));
|
||||
res.status(500).send(
|
||||
'Internal Server Error - Failed to delete room: ' +
|
||||
(error instanceof Error ? error.message : 'Unknown error')
|
||||
);
|
||||
return;
|
||||
}
|
||||
};
|
||||
@ -105,7 +114,10 @@ export const deleteAllRoomsCtrl = async (_req: Request, res: Response) => {
|
||||
message: error instanceof Error ? error.message : 'Unknown error',
|
||||
stack: error instanceof Error ? error.stack : 'No stack trace'
|
||||
});
|
||||
res.status(500).send('Internal Server Error - Failed to delete all rooms: ' + (error instanceof Error ? error.message : 'Unknown error'));
|
||||
res.status(500).send(
|
||||
'Internal Server Error - Failed to delete all rooms: ' +
|
||||
(error instanceof Error ? error.message : 'Unknown error')
|
||||
);
|
||||
return;
|
||||
}
|
||||
};
|
||||
@ -133,7 +145,10 @@ export const deleteAllRecordingsCtrl = async (_req: Request, res: Response) => {
|
||||
message: error instanceof Error ? error.message : 'Unknown error',
|
||||
stack: error instanceof Error ? error.stack : 'No stack trace'
|
||||
});
|
||||
res.status(500).send('Internal Server Error - Failed to delete all recordings: ' + (error instanceof Error ? error.message : 'Unknown error'));
|
||||
res.status(500).send(
|
||||
'Internal Server Error - Failed to delete all recordings: ' +
|
||||
(error instanceof Error ? error.message : 'Unknown error')
|
||||
);
|
||||
return;
|
||||
}
|
||||
};
|
||||
@ -150,7 +165,7 @@ const processFormPreferences = (body: any): any => {
|
||||
enabled: body['preferences.recordingPreferences.enabled'] === 'on',
|
||||
// Only include allowAccessTo if recording is enabled
|
||||
...(body['preferences.recordingPreferences.enabled'] === 'on' && {
|
||||
allowAccessTo: body['preferences.recordingPreferences.allowAccessTo'] || 'admin-moderator-publisher'
|
||||
allowAccessTo: body['preferences.recordingPreferences.allowAccessTo'] || 'admin-moderator-speaker'
|
||||
})
|
||||
},
|
||||
virtualBackgroundPreferences: {
|
||||
|
||||
@ -32,5 +32,5 @@ export interface ParticipantPermissions {
|
||||
*/
|
||||
export const enum ParticipantRole {
|
||||
MODERATOR = 'moderator',
|
||||
PUBLISHER = 'publisher'
|
||||
SPEAKER = 'speaker'
|
||||
}
|
||||
|
||||
@ -2,29 +2,29 @@
|
||||
* Interface representing the preferences for a room.
|
||||
*/
|
||||
export interface MeetRoomPreferences {
|
||||
chatPreferences: MeetChatPreferences;
|
||||
recordingPreferences: MeetRecordingPreferences;
|
||||
virtualBackgroundPreferences: MeetVirtualBackgroundPreferences;
|
||||
chatPreferences: MeetChatPreferences;
|
||||
recordingPreferences: MeetRecordingPreferences;
|
||||
virtualBackgroundPreferences: MeetVirtualBackgroundPreferences;
|
||||
}
|
||||
|
||||
/**
|
||||
* Interface representing the preferences for recording.
|
||||
*/
|
||||
export interface MeetRecordingPreferences {
|
||||
enabled: boolean;
|
||||
allowAccessTo?: MeetRecordingAccess;
|
||||
enabled: boolean;
|
||||
allowAccessTo?: MeetRecordingAccess;
|
||||
}
|
||||
|
||||
export const enum MeetRecordingAccess {
|
||||
ADMIN = 'admin', // Only admins can access the recording
|
||||
ADMIN_MODERATOR = 'admin-moderator', // Admins and moderators can access
|
||||
ADMIN_MODERATOR_PUBLISHER = 'admin-moderator-publisher', // Admins, moderators and publishers can access
|
||||
ADMIN = 'admin', // Only admins can access the recording
|
||||
ADMIN_MODERATOR = 'admin-moderator', // Admins and moderators can access
|
||||
ADMIN_MODERATOR_SPEAKER = 'admin-moderator-speaker' // Admins, moderators and speakers can access
|
||||
}
|
||||
|
||||
export interface MeetChatPreferences {
|
||||
enabled: boolean;
|
||||
enabled: boolean;
|
||||
}
|
||||
|
||||
export interface MeetVirtualBackgroundPreferences {
|
||||
enabled: boolean;
|
||||
enabled: boolean;
|
||||
}
|
||||
|
||||
@ -21,7 +21,7 @@ export interface MeetRoom extends BaseRoomOptions {
|
||||
roomName: string;
|
||||
creationDate: number;
|
||||
moderatorRoomUrl: string;
|
||||
publisherRoomUrl: string;
|
||||
speakerRoomUrl: string;
|
||||
markedForDeletion?: boolean;
|
||||
}
|
||||
|
||||
|
||||
@ -1,27 +1,26 @@
|
||||
export enum WebComponentProperty {
|
||||
/**
|
||||
* The OpenVidu Meet room URL to connect to (moderator or speaker url)
|
||||
* @required This attribute is required unless `recording-url` is provided.
|
||||
*/
|
||||
ROOM_URL = 'room-url',
|
||||
/**
|
||||
* The URL of a recording to view.
|
||||
* @required This attribute is required unless `room-url` is provided.
|
||||
*/
|
||||
RECORDING_URL = 'recording-url',
|
||||
/**
|
||||
* Display name for the local participant.
|
||||
*/
|
||||
PARTICIPANT_NAME = 'participant-name',
|
||||
|
||||
/**
|
||||
* The OpenVidu Meet room URL to connect to (moderator or publisher url)
|
||||
* @required This attribute is required unless `recording-url` is provided.
|
||||
*/
|
||||
ROOM_URL = 'room-url',
|
||||
/**
|
||||
* The URL of a recording to view.
|
||||
* @required This attribute is required unless `room-url` is provided.
|
||||
*/
|
||||
RECORDING_URL = 'recording-url',
|
||||
/**
|
||||
* Display name for the local participant.
|
||||
*/
|
||||
PARTICIPANT_NAME = 'participant-name',
|
||||
|
||||
/**
|
||||
* URL to redirect to when leaving the meeting.
|
||||
* Redirection occurs after the **`CLOSED` event** fires.
|
||||
*/
|
||||
LEAVE_REDIRECT_URL = 'leave-redirect-url',
|
||||
/**
|
||||
* Whether to show only recordings instead of live meetings.
|
||||
*/
|
||||
SHOW_ONLY_RECORDINGS = 'show-only-recordings'
|
||||
/**
|
||||
* URL to redirect to when leaving the meeting.
|
||||
* Redirection occurs after the **`CLOSED` event** fires.
|
||||
*/
|
||||
LEAVE_REDIRECT_URL = 'leave-redirect-url',
|
||||
/**
|
||||
* Whether to show only recordings instead of live meetings.
|
||||
*/
|
||||
SHOW_ONLY_RECORDINGS = 'show-only-recordings'
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user