Rename publisher role to speaker across the application

This commit is contained in:
juancarmore 2025-08-06 22:14:56 +02:00
parent 69b5bf3071
commit 1161f1bb21
57 changed files with 756 additions and 749 deletions

View File

@ -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]
```

View File

@ -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']

View File

@ -19,7 +19,7 @@ content:
canRecord: true
canChat: true
canChangeVirtualBackground: true
- role: 'publisher'
- role: 'speaker'
permissions:
livekit:
roomJoin: true

View File

@ -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'

View File

@ -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

View File

@ -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

View File

@ -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:

View File

@ -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.

View File

@ -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"
},

View File

@ -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);

View File

@ -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 };
}
/**

View File

@ -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);

View File

@ -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 }
})

View File

@ -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,

View File

@ -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,

View File

@ -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
}

View File

@ -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,

View File

@ -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
};
};

View File

@ -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]
);
});

View File

@ -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
);
});

View File

@ -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);

View File

@ -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!);

View File

@ -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);

View File

@ -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);

View File

@ -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 }

View File

@ -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);

View File

@ -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 }

View File

@ -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 () => {

View File

@ -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();

View File

@ -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 }

View File

@ -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);
});
});

View File

@ -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);

View File

@ -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)

View File

@ -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);
});

View File

@ -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>

View File

@ -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]]);
});
});

View File

@ -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) {

View File

@ -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
}

View File

@ -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'
}
];

View File

@ -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) {

View File

@ -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',

View File

@ -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>
}

View File

@ -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) {

View File

@ -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;

View File

@ -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 }

View File

@ -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);

View File

@ -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');

View File

@ -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' });
});

View File

@ -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);

View File

@ -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();

View File

@ -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');
});
});

View File

@ -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>

View File

@ -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: {

View File

@ -32,5 +32,5 @@ export interface ParticipantPermissions {
*/
export const enum ParticipantRole {
MODERATOR = 'moderator',
PUBLISHER = 'publisher'
SPEAKER = 'speaker'
}

View File

@ -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;
}

View File

@ -21,7 +21,7 @@ export interface MeetRoom extends BaseRoomOptions {
roomName: string;
creationDate: number;
moderatorRoomUrl: string;
publisherRoomUrl: string;
speakerRoomUrl: string;
markedForDeletion?: boolean;
}

View File

@ -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'
}