Compare commits
234 Commits
update-lay
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
029802787a | ||
|
|
ad413d2791 | ||
|
|
d025a35e15 | ||
|
|
1761517afc | ||
|
|
9278260837 | ||
|
|
53f62708ce | ||
|
|
ba0d0b10c4 | ||
|
|
8953e9891c | ||
|
|
02703b1f83 | ||
|
|
c808e98820 | ||
|
|
56d5126acb | ||
|
|
ac3a728591 | ||
|
|
90a1c6fde9 | ||
|
|
7378a8f53e | ||
|
|
3142f9fe79 | ||
|
|
2a1575768f | ||
|
|
396c23aa3c | ||
|
|
96e441726c | ||
|
|
a853aa02a2 | ||
|
|
366632741c | ||
|
|
1046b5a0dd | ||
|
|
59d722f882 | ||
|
|
b08bb10f63 | ||
|
|
177648e6d5 | ||
|
|
b059b88be4 | ||
|
|
4e634dac54 | ||
|
|
b0c7dcbc9a | ||
|
|
ba7600bfc5 | ||
|
|
accb35c7e1 | ||
|
|
1add921ce0 | ||
|
|
2fe720c90b | ||
|
|
3f91e281b3 | ||
|
|
4ac182c244 | ||
|
|
43f7ff5001 | ||
|
|
30bd4b5a41 | ||
|
|
00433c75a4 | ||
|
|
becf3070b0 | ||
|
|
21e939e09c | ||
|
|
659cdcaf73 | ||
|
|
dbcc9bbb25 | ||
|
|
215b11e93f | ||
|
|
55aab084b0 | ||
|
|
96b5cd249e | ||
|
|
4751e7e989 | ||
|
|
0a56a74433 | ||
|
|
cb12d9a8fe | ||
|
|
9ae27bf32a | ||
|
|
f677b18879 | ||
|
|
f95b02e42b | ||
|
|
5f8af67ac6 | ||
|
|
1ef813e509 | ||
|
|
011e44b4f9 | ||
|
|
ed057612a0 | ||
|
|
5cdc49d90c | ||
|
|
073f0dc640 | ||
|
|
bbd4d5fbaf | ||
|
|
5f722c36e7 | ||
|
|
b5ccd7c087 | ||
|
|
13a339eb87 | ||
|
|
1d5cd9be26 | ||
|
|
68a10ff901 | ||
|
|
eabb559a82 | ||
|
|
e70dc6619f | ||
|
|
520816b983 | ||
|
|
b24992ad24 | ||
|
|
d9e064e971 | ||
|
|
8af00ab6ee | ||
|
|
2ec42f701d | ||
|
|
f77630d1e0 | ||
|
|
3cb163deee | ||
|
|
4ecd086f21 | ||
|
|
a1acc9ba22 | ||
|
|
0368ab83e6 | ||
|
|
082aa8480c | ||
|
|
ca2d41b05e | ||
|
|
db62cf0e1c | ||
|
|
3eb06e41e2 | ||
|
|
56e025d23d | ||
|
|
6c730a6dbc | ||
|
|
163e0d5f99 | ||
|
|
834dc2be42 | ||
|
|
7cddb59e2d | ||
|
|
8afed3a2f8 | ||
|
|
dac64bb1a9 | ||
|
|
c42a3ce1cf | ||
|
|
6455a4937c | ||
|
|
4bee373e85 | ||
|
|
e0c0453a02 | ||
|
|
7a83cc57fd | ||
|
|
7d6f61e12c | ||
|
|
6f841eb254 | ||
|
|
be5e3ffb1d | ||
|
|
0ba9d0b297 | ||
|
|
caad4bc550 | ||
|
|
450aa85b88 | ||
|
|
86af733b37 | ||
|
|
8d685e6ae5 | ||
|
|
05980df465 | ||
|
|
744fd05724 | ||
|
|
d1431687f0 | ||
|
|
87b86652dc | ||
|
|
115993fe69 | ||
|
|
f65f5dd33d | ||
|
|
d78eae9810 | ||
|
|
8fbe8fb716 | ||
|
|
6a9bd2be95 | ||
|
|
320135dd39 | ||
|
|
0ca445a89b | ||
|
|
76ea36cf69 | ||
|
|
b0b95f38a8 | ||
|
|
6ad700f538 | ||
|
|
11dcd1238a | ||
|
|
8f858267b4 | ||
|
|
cea8301e00 | ||
|
|
f2c4df0de6 | ||
|
|
eb3e11c37c | ||
|
|
f5b805f3a3 | ||
|
|
1c955c60d0 | ||
|
|
113dbe4f88 | ||
|
|
f6d685a158 | ||
|
|
60cb1afdf3 | ||
|
|
b4f482f9d7 | ||
|
|
3a83efa668 | ||
|
|
0ab6a48e13 | ||
|
|
9a853f285b | ||
|
|
1d552c9098 | ||
|
|
c806e90e42 | ||
|
|
3a2ce89a3d | ||
|
|
7c75a9ddd0 | ||
|
|
4d3adc1922 | ||
|
|
d925cb7843 | ||
|
|
59ea1f881b | ||
|
|
d059872667 | ||
|
|
bd1c67ba31 | ||
|
|
f178f2a611 | ||
|
|
477119a06f | ||
|
|
cbe07bcf4f | ||
|
|
34f50d0dee | ||
|
|
9ff200fa88 | ||
|
|
d7cefdfd47 | ||
|
|
c93b369a4f | ||
|
|
cf3f03bf3c | ||
|
|
2761e68dd8 | ||
|
|
a3d4fda6ae | ||
|
|
1abdc45ff3 | ||
|
|
5ef051658a | ||
|
|
c51a5173dd | ||
|
|
07b22a0d29 | ||
|
|
1663b008ed | ||
|
|
f930bf1447 | ||
|
|
8e9443351c | ||
|
|
6c7bfd4d3f | ||
|
|
68c5ce7cd2 | ||
|
|
5638025211 | ||
|
|
da7759d249 | ||
|
|
ba374ce229 | ||
|
|
cf84de4221 | ||
|
|
39a9b7da02 | ||
|
|
e990c19672 | ||
|
|
66b7f6026b | ||
|
|
21971e1895 | ||
|
|
246d6e91a4 | ||
|
|
d20571b0ef | ||
|
|
f6abd1cb4c | ||
|
|
bd021c9576 | ||
|
|
c3ca84ad66 | ||
|
|
153af9c673 | ||
|
|
c8cfb6598e | ||
|
|
b177b3b02e | ||
|
|
f83a5d8942 | ||
|
|
40475dc372 | ||
|
|
fd998e7b6b | ||
|
|
8ccc5d1a8b | ||
|
|
f71b567823 | ||
|
|
a9360ef452 | ||
|
|
3aaf976964 | ||
|
|
73813feb38 | ||
|
|
bdbc6d02ad | ||
|
|
848cf2ca17 | ||
|
|
49b44d0353 | ||
|
|
0d6838019d | ||
|
|
253b435fbe | ||
|
|
447a1fc9e5 | ||
|
|
5234b28917 | ||
|
|
69df748002 | ||
|
|
b711840349 | ||
|
|
69399bb2ff | ||
|
|
77528b28f4 | ||
|
|
fad38b696d | ||
|
|
e3fe104b05 | ||
|
|
4f9116707c | ||
|
|
3f25ba6f74 | ||
|
|
496591695a | ||
|
|
45ee463bc6 | ||
|
|
3ae55d0814 | ||
|
|
f74b50d5c8 | ||
|
|
0f237af827 | ||
|
|
e30aa5f1a5 | ||
|
|
f62e59168d | ||
|
|
99283ab63d | ||
|
|
6b3ef47f08 | ||
|
|
152a877054 | ||
|
|
3665c70105 | ||
|
|
294bd1b7ec | ||
|
|
8d12105b91 | ||
|
|
28b58a80bf | ||
|
|
a518b16b6b | ||
|
|
6eb33c6198 | ||
|
|
a56a119993 | ||
|
|
26bc6cdfc1 | ||
|
|
f3de3e0fa3 | ||
|
|
72119685ad | ||
|
|
84b74ea0f3 | ||
|
|
9fe20f6e87 | ||
|
|
27094be54e | ||
|
|
d377b016e2 | ||
|
|
5b94728bc9 | ||
|
|
5131f9e717 | ||
|
|
32a9a31d8b | ||
|
|
ae5907cbbd | ||
|
|
d9a31edb60 | ||
|
|
d3554aa6cb | ||
|
|
3ec9b43ce4 | ||
|
|
253ad630c7 | ||
|
|
d72149c97d | ||
|
|
449f9cbf25 | ||
|
|
b055ef0333 | ||
|
|
1d55311757 | ||
|
|
d52524241f | ||
|
|
3fdf2144ab | ||
|
|
e6d04aca16 | ||
|
|
684d575c08 | ||
|
|
7dd368476e | ||
|
|
fd905286a6 |
@ -141,8 +141,6 @@
|
|||||||
**/*.mov
|
**/*.mov
|
||||||
**/*.mkv
|
**/*.mkv
|
||||||
**/*.webm
|
**/*.webm
|
||||||
**/*.mp3
|
|
||||||
**/*.wav
|
|
||||||
**/*.flac
|
**/*.flac
|
||||||
|
|
||||||
# ====================================================
|
# ====================================================
|
||||||
|
|||||||
53
.github/workflows/backend-integration-test.yaml
vendored
53
.github/workflows/backend-integration-test.yaml
vendored
@ -2,14 +2,14 @@ name: Backend Integration Tests
|
|||||||
on:
|
on:
|
||||||
push:
|
push:
|
||||||
paths:
|
paths:
|
||||||
- 'backend/src/**'
|
- 'meet-ce/backend/src/**'
|
||||||
- 'backend/package.json'
|
- 'meet-ce/backend/package.json'
|
||||||
- 'backend/pnpm-lock.yaml'
|
- 'pnpm-lock.yaml'
|
||||||
- 'backend/tests/**'
|
- 'meet-ce/backend/tests/**'
|
||||||
- '.github/workflows/backend-integration-test.yaml'
|
- '.github/workflows/backend-integration-test.yaml'
|
||||||
pull_request:
|
pull_request:
|
||||||
paths:
|
paths:
|
||||||
- 'backend/src/**'
|
- 'meet-ce/backend/src/**'
|
||||||
workflow_dispatch:
|
workflow_dispatch:
|
||||||
inputs:
|
inputs:
|
||||||
use-aws:
|
use-aws:
|
||||||
@ -22,7 +22,7 @@ on:
|
|||||||
jobs:
|
jobs:
|
||||||
build-components:
|
build-components:
|
||||||
name: Build OpenVidu Components Angular
|
name: Build OpenVidu Components Angular
|
||||||
runs-on: ov-actions-runner
|
runs-on: ${{ vars.LABEL_WORKER_SELFHOSTED }}
|
||||||
steps:
|
steps:
|
||||||
- name: Build Components
|
- name: Build Components
|
||||||
id: build
|
id: build
|
||||||
@ -33,26 +33,19 @@ jobs:
|
|||||||
test-api:
|
test-api:
|
||||||
name: ${{ matrix.test-name }}
|
name: ${{ matrix.test-name }}
|
||||||
needs: build-components
|
needs: build-components
|
||||||
runs-on: ov-actions-runner
|
runs-on: ${{ vars.LABEL_WORKER_SELFHOSTED }}
|
||||||
strategy:
|
strategy:
|
||||||
fail-fast: false
|
fail-fast: false
|
||||||
matrix:
|
matrix:
|
||||||
include:
|
include:
|
||||||
- test-name: 'Rooms API Tests'
|
- test-name: 'Room Management API Tests (Rooms, Meetings)'
|
||||||
test-script: 'test:integration-backend-rooms'
|
test-script: 'test:integration-backend-room-management'
|
||||||
- test-name: 'Webhook Tests'
|
- test-name: 'Webhook Tests'
|
||||||
test-script: 'test:integration-backend-webhooks'
|
test-script: 'test:integration-backend-webhooks'
|
||||||
- test-name: 'Security API Tests'
|
- test-name: 'Auth & Security API Tests (Security, Auth, API Keys, Users)'
|
||||||
test-script: 'test:integration-backend-security'
|
test-script: 'test:integration-backend-auth-security'
|
||||||
azure-container: 'openvidu-appdata-security'
|
- test-name: 'Config & Analytics API Tests (Global Config, Analytics)'
|
||||||
- test-name: 'Global Config API Tests'
|
test-script: 'test:integration-backend-config-analytics'
|
||||||
test-script: 'test:integration-backend-global-config'
|
|
||||||
- test-name: 'Participants API Tests'
|
|
||||||
test-script: 'test:integration-backend-participants'
|
|
||||||
- test-name: 'Meetings API Tests'
|
|
||||||
test-script: 'test:integration-backend-meetings'
|
|
||||||
- test-name: 'Users API Tests'
|
|
||||||
test-script: 'test:integration-backend-users'
|
|
||||||
steps:
|
steps:
|
||||||
- name: Install LK CLI
|
- name: Install LK CLI
|
||||||
run: curl -sSL https://get.livekit.io/cli | bash
|
run: curl -sSL https://get.livekit.io/cli | bash
|
||||||
@ -85,13 +78,11 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
build_components_angular: 'true'
|
build_components_angular: 'true'
|
||||||
components_artifact_name: ${{ needs.build-components.outputs.artifact_name }}
|
components_artifact_name: ${{ needs.build-components.outputs.artifact_name }}
|
||||||
env:
|
|
||||||
MEET_AZURE_CONTAINER_NAME: ${{ matrix.azure-container || '' }}
|
|
||||||
|
|
||||||
- name: Run tests
|
- name: Run tests
|
||||||
run: pnpm run ${{ matrix.test-script }}
|
run: pnpm run ${{ matrix.test-script }}
|
||||||
env:
|
env:
|
||||||
JEST_JUNIT_OUTPUT_DIR: './backend/reports/'
|
JEST_JUNIT_OUTPUT_DIR: './meet-ce/backend/reports/'
|
||||||
|
|
||||||
- name: Upload OpenVidu Meet logs
|
- name: Upload OpenVidu Meet logs
|
||||||
if: failure()
|
if: failure()
|
||||||
@ -115,7 +106,7 @@ jobs:
|
|||||||
|
|
||||||
start-aws-runner-s3:
|
start-aws-runner-s3:
|
||||||
name: Prepare AWS runner (S3)
|
name: Prepare AWS runner (S3)
|
||||||
runs-on: ov-actions-runner
|
runs-on: ${{ vars.LABEL_WORKER_SELFHOSTED }}
|
||||||
if: ${{ inputs.use-aws != 'false' }}
|
if: ${{ inputs.use-aws != 'false' }}
|
||||||
outputs:
|
outputs:
|
||||||
label: ${{ steps.start-ec2-runner.outputs.label }}
|
label: ${{ steps.start-ec2-runner.outputs.label }}
|
||||||
@ -138,7 +129,7 @@ jobs:
|
|||||||
|
|
||||||
start-aws-runner-abs:
|
start-aws-runner-abs:
|
||||||
name: Prepare AWS runner (ABS)
|
name: Prepare AWS runner (ABS)
|
||||||
runs-on: ov-actions-runner
|
runs-on: ${{ vars.LABEL_WORKER_SELFHOSTED }}
|
||||||
if: ${{ inputs.use-aws != 'false' }}
|
if: ${{ inputs.use-aws != 'false' }}
|
||||||
outputs:
|
outputs:
|
||||||
label: ${{ steps.start-ec2-runner.outputs.label }}
|
label: ${{ steps.start-ec2-runner.outputs.label }}
|
||||||
@ -161,7 +152,7 @@ jobs:
|
|||||||
|
|
||||||
start-aws-runner-gcs:
|
start-aws-runner-gcs:
|
||||||
name: Prepare AWS runner (GCS)
|
name: Prepare AWS runner (GCS)
|
||||||
runs-on: ov-actions-runner
|
runs-on: ${{ vars.LABEL_WORKER_SELFHOSTED }}
|
||||||
if: ${{ inputs.use-aws != 'false' }}
|
if: ${{ inputs.use-aws != 'false' }}
|
||||||
outputs:
|
outputs:
|
||||||
label: ${{ steps.start-ec2-runner.outputs.label }}
|
label: ${{ steps.start-ec2-runner.outputs.label }}
|
||||||
@ -190,7 +181,7 @@ jobs:
|
|||||||
- start-aws-runner-gcs
|
- start-aws-runner-gcs
|
||||||
- build-components
|
- build-components
|
||||||
if: ${{ always() && (needs.start-aws-runner-s3.result == 'success' || needs.start-aws-runner-s3.result == 'skipped') && (needs.start-aws-runner-abs.result == 'success' || needs.start-aws-runner-abs.result == 'skipped') && (needs.start-aws-runner-gcs.result == 'success' || needs.start-aws-runner-gcs.result == 'skipped') }}
|
if: ${{ always() && (needs.start-aws-runner-s3.result == 'success' || needs.start-aws-runner-s3.result == 'skipped') && (needs.start-aws-runner-abs.result == 'success' || needs.start-aws-runner-abs.result == 'skipped') && (needs.start-aws-runner-gcs.result == 'success' || needs.start-aws-runner-gcs.result == 'skipped') }}
|
||||||
runs-on: ${{ (matrix.storage-provider == 's3' && needs.start-aws-runner-s3.outputs.label) || (matrix.storage-provider == 'abs' && needs.start-aws-runner-abs.outputs.label) || (matrix.storage-provider == 'gcs' && needs.start-aws-runner-gcs.outputs.label) || 'ov-actions-runner' }}
|
runs-on: ${{ (matrix.storage-provider == 's3' && needs.start-aws-runner-s3.outputs.label) || (matrix.storage-provider == 'abs' && needs.start-aws-runner-abs.outputs.label) || (matrix.storage-provider == 'gcs' && needs.start-aws-runner-gcs.outputs.label) || vars.LABEL_WORKER_SELFHOSTED }}
|
||||||
strategy:
|
strategy:
|
||||||
fail-fast: false
|
fail-fast: false
|
||||||
matrix:
|
matrix:
|
||||||
@ -292,7 +283,7 @@ jobs:
|
|||||||
- name: Run tests
|
- name: Run tests
|
||||||
run: pnpm run test:integration-backend-recordings
|
run: pnpm run test:integration-backend-recordings
|
||||||
env:
|
env:
|
||||||
JEST_JUNIT_OUTPUT_DIR: './backend/reports/'
|
JEST_JUNIT_OUTPUT_DIR: './meet-ce/backend/reports/'
|
||||||
MEET_BLOB_STORAGE_MODE: ${{ matrix.storage-provider }}
|
MEET_BLOB_STORAGE_MODE: ${{ matrix.storage-provider }}
|
||||||
# ABS variables
|
# ABS variables
|
||||||
MEET_AZURE_ACCOUNT_NAME: ${{ matrix.storage-provider == 'abs' && vars.MEET_AZURE_ACCOUNT_NAME || '' }}
|
MEET_AZURE_ACCOUNT_NAME: ${{ matrix.storage-provider == 'abs' && vars.MEET_AZURE_ACCOUNT_NAME || '' }}
|
||||||
@ -315,7 +306,7 @@ jobs:
|
|||||||
uses: actions/upload-artifact@v4
|
uses: actions/upload-artifact@v4
|
||||||
with:
|
with:
|
||||||
name: recordings-test-${{ matrix.storage-provider }}-openvidu-meet-logs
|
name: recordings-test-${{ matrix.storage-provider }}-openvidu-meet-logs
|
||||||
path: backend/meet_backend.log
|
path: meet_backend.log
|
||||||
retention-days: 2
|
retention-days: 2
|
||||||
|
|
||||||
- name: Clean up credentials
|
- name: Clean up credentials
|
||||||
@ -335,7 +326,7 @@ jobs:
|
|||||||
- start-aws-runner-abs
|
- start-aws-runner-abs
|
||||||
- start-aws-runner-gcs
|
- start-aws-runner-gcs
|
||||||
- test-recordings
|
- test-recordings
|
||||||
runs-on: ov-actions-runner
|
runs-on: ${{ vars.LABEL_WORKER_SELFHOSTED }}
|
||||||
if: ${{ always() }}
|
if: ${{ always() }}
|
||||||
strategy:
|
strategy:
|
||||||
fail-fast: false
|
fail-fast: false
|
||||||
@ -359,7 +350,7 @@ jobs:
|
|||||||
- build-components
|
- build-components
|
||||||
- test-api
|
- test-api
|
||||||
- test-recordings
|
- test-recordings
|
||||||
runs-on: ov-actions-runner
|
runs-on: ${{ vars.LABEL_WORKER_SELFHOSTED }}
|
||||||
if: ${{ always() }}
|
if: ${{ always() }}
|
||||||
steps:
|
steps:
|
||||||
- name: Remove Artifact
|
- name: Remove Artifact
|
||||||
|
|||||||
2
.github/workflows/backend-unit-test.yaml
vendored
2
.github/workflows/backend-unit-test.yaml
vendored
@ -7,7 +7,7 @@ on:
|
|||||||
jobs:
|
jobs:
|
||||||
unit-test:
|
unit-test:
|
||||||
name: Backend Unit Tests
|
name: Backend Unit Tests
|
||||||
runs-on: ov-actions-runner
|
runs-on: ${{ vars.LABEL_WORKER_SELFHOSTED }}
|
||||||
steps:
|
steps:
|
||||||
- name: Setup Node.js
|
- name: Setup Node.js
|
||||||
uses: actions/setup-node@v5
|
uses: actions/setup-node@v5
|
||||||
|
|||||||
10
.github/workflows/wc-e2e-test.yaml
vendored
10
.github/workflows/wc-e2e-test.yaml
vendored
@ -7,7 +7,7 @@ on:
|
|||||||
jobs:
|
jobs:
|
||||||
e2e-tests:
|
e2e-tests:
|
||||||
name: WebComponent E2E Tests
|
name: WebComponent E2E Tests
|
||||||
runs-on: ov-actions-runner
|
runs-on: ${{ vars.LABEL_WORKER_SELFHOSTED }}
|
||||||
steps:
|
steps:
|
||||||
- name: Setup Node.js
|
- name: Setup Node.js
|
||||||
uses: actions/setup-node@v5
|
uses: actions/setup-node@v5
|
||||||
@ -17,6 +17,8 @@ jobs:
|
|||||||
uses: pnpm/action-setup@v4
|
uses: pnpm/action-setup@v4
|
||||||
with:
|
with:
|
||||||
version: 10.18.3
|
version: 10.18.3
|
||||||
|
- name: Install LK CLI
|
||||||
|
run: curl -sSL https://get.livekit.io/cli | bash
|
||||||
- name: Setup OpenVidu Local Deployment
|
- name: Setup OpenVidu Local Deployment
|
||||||
uses: OpenVidu/actions/start-openvidu-local-deployment@main
|
uses: OpenVidu/actions/start-openvidu-local-deployment@main
|
||||||
with:
|
with:
|
||||||
@ -56,7 +58,7 @@ jobs:
|
|||||||
uses: actions/upload-artifact@v4
|
uses: actions/upload-artifact@v4
|
||||||
with:
|
with:
|
||||||
name: room-test-results
|
name: room-test-results
|
||||||
path: frontend/webcomponent/test-results/
|
path: meet-ce/frontend/webcomponent/test-results/
|
||||||
retention-days: 2
|
retention-days: 2
|
||||||
|
|
||||||
- name: Upload TestApp logs on failure
|
- name: Upload TestApp logs on failure
|
||||||
@ -64,7 +66,7 @@ jobs:
|
|||||||
uses: actions/upload-artifact@v4
|
uses: actions/upload-artifact@v4
|
||||||
with:
|
with:
|
||||||
name: room-test-testapp-logs
|
name: room-test-testapp-logs
|
||||||
path: testapp/testapp.log
|
path: testapp.log
|
||||||
retention-days: 2
|
retention-days: 2
|
||||||
|
|
||||||
- name: Upload OpenVidu Meet logs on failure
|
- name: Upload OpenVidu Meet logs on failure
|
||||||
@ -72,7 +74,7 @@ jobs:
|
|||||||
uses: actions/upload-artifact@v4
|
uses: actions/upload-artifact@v4
|
||||||
with:
|
with:
|
||||||
name: room-test-openvidu-meet-logs
|
name: room-test-openvidu-meet-logs
|
||||||
path: backend/meet_backend.log
|
path: meet_backend.log
|
||||||
retention-days: 2
|
retention-days: 2
|
||||||
|
|
||||||
- name: Clean up
|
- name: Clean up
|
||||||
|
|||||||
2
.github/workflows/wc-unit-test.yaml
vendored
2
.github/workflows/wc-unit-test.yaml
vendored
@ -7,7 +7,7 @@ on:
|
|||||||
jobs:
|
jobs:
|
||||||
unit-test:
|
unit-test:
|
||||||
name: WebComponent Unit Tests
|
name: WebComponent Unit Tests
|
||||||
runs-on: ov-actions-runner
|
runs-on: ${{ vars.LABEL_WORKER_SELFHOSTED }}
|
||||||
steps:
|
steps:
|
||||||
- name: Setup Node.js
|
- name: Setup Node.js
|
||||||
uses: actions/setup-node@v5
|
uses: actions/setup-node@v5
|
||||||
|
|||||||
2
.gitignore
vendored
2
.gitignore
vendored
@ -37,7 +37,7 @@ pnpm-debug.log*
|
|||||||
|
|
||||||
|
|
||||||
**/**/test-results
|
**/**/test-results
|
||||||
**/**/public/
|
**/backend/public/
|
||||||
|
|
||||||
**/*/coverage
|
**/*/coverage
|
||||||
**/**/test-results
|
**/**/test-results
|
||||||
|
|||||||
52
README.md
52
README.md
@ -8,8 +8,8 @@ OpenVidu Meet is a fully featured video conferencing application built with Angu
|
|||||||
2. [Prerequisites](#prerequisites)
|
2. [Prerequisites](#prerequisites)
|
||||||
3. [Getting Started](#getting-started)
|
3. [Getting Started](#getting-started)
|
||||||
4. [Development](#development)
|
4. [Development](#development)
|
||||||
- [Development Mode](#development-mode)
|
- [Development Mode](#development-mode)
|
||||||
- [Manual Development Setup](#manual-development-setup)
|
- [Manual Development Setup](#manual-development-setup)
|
||||||
5. [Building](#building)
|
5. [Building](#building)
|
||||||
6. [Testing](#testing)
|
6. [Testing](#testing)
|
||||||
7. [Documentation](#documentation)
|
7. [Documentation](#documentation)
|
||||||
@ -26,12 +26,12 @@ The OpenVidu Meet application is a monorepo managed with **pnpm workspaces** and
|
|||||||
### Core Components
|
### Core Components
|
||||||
|
|
||||||
- **Frontend** (`frontend/`): Angular 20 application providing the user interface
|
- **Frontend** (`frontend/`): Angular 20 application providing the user interface
|
||||||
- **shared-meet-components**: Reusable Angular library with shared components for administration and preferences
|
- **shared-meet-components**: Reusable Angular library with shared components for administration and preferences
|
||||||
- Integrates [openvidu-components-angular](https://github.com/OpenVidu/openvidu/tree/master/openvidu-components-angular) for core video conferencing functionality
|
- Integrates [openvidu-components-angular](https://github.com/OpenVidu/openvidu/tree/master/openvidu-components-angular) for core video conferencing functionality
|
||||||
|
|
||||||
- **Backend** (`backend/`): Node.js/TypeScript REST API server
|
- **Backend** (`backend/`): Node.js/TypeScript REST API server
|
||||||
- Manages rooms, participants, recordings, and authentication
|
- Manages rooms, participants, recordings, and authentication
|
||||||
- Serves the compiled frontend in production
|
- Serves the compiled frontend in production
|
||||||
|
|
||||||
- **Typings** (`typings/`): Shared TypeScript type definitions used across frontend and backend
|
- **Typings** (`typings/`): Shared TypeScript type definitions used across frontend and backend
|
||||||
|
|
||||||
@ -46,10 +46,9 @@ Before starting, ensure you have the following installed:
|
|||||||
- **Node.js**: Version 22 or higher
|
- **Node.js**: Version 22 or higher
|
||||||
- **pnpm**: Package manager (will be installed automatically by meet.sh if missing)
|
- **pnpm**: Package manager (will be installed automatically by meet.sh if missing)
|
||||||
- **LiveKit**: For local testing (optional)
|
- **LiveKit**: For local testing (optional)
|
||||||
```bash
|
```bash
|
||||||
curl -sSL https://get.livekit.io/cli | bash
|
curl -sSL https://get.livekit.io/cli | bash
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
||||||
## Getting Started
|
## Getting Started
|
||||||
|
|
||||||
@ -82,7 +81,7 @@ cd openvidu-meet
|
|||||||
|
|
||||||
Then, the application will be available at [http://localhost:6080](http://localhost:6080).
|
Then, the application will be available at [http://localhost:6080](http://localhost:6080).
|
||||||
|
|
||||||
> **Note:** Livereload is also available at [http://localhost:5080](http://localhost:5080).
|
> **Note:** Livereload is also available at [http://localhost:6081](http://localhost:6081).
|
||||||
|
|
||||||
## Development
|
## Development
|
||||||
|
|
||||||
@ -95,6 +94,7 @@ The recommended way to develop is using the integrated development mode that wat
|
|||||||
```
|
```
|
||||||
|
|
||||||
This command starts concurrent watchers for:
|
This command starts concurrent watchers for:
|
||||||
|
|
||||||
- **openvidu-components-angular**: Core Angular components library
|
- **openvidu-components-angular**: Core Angular components library
|
||||||
- **Typings**: Shared type definitions with automatic sync
|
- **Typings**: Shared type definitions with automatic sync
|
||||||
- **Backend**: Node.js server with nodemon auto-restart
|
- **Backend**: Node.js server with nodemon auto-restart
|
||||||
@ -103,6 +103,7 @@ This command starts concurrent watchers for:
|
|||||||
|
|
||||||
> [!NOTE]
|
> [!NOTE]
|
||||||
> The backend uses `backend/.env.development` for environment variables during development. Configure your LiveKit credentials there:
|
> The backend uses `backend/.env.development` for environment variables during development. Configure your LiveKit credentials there:
|
||||||
|
>
|
||||||
> ```env
|
> ```env
|
||||||
> LIVEKIT_URL=ws://localhost:7880
|
> LIVEKIT_URL=ws://localhost:7880
|
||||||
> LIVEKIT_API_KEY=your-api-key
|
> LIVEKIT_API_KEY=your-api-key
|
||||||
@ -170,6 +171,7 @@ The `meet.sh` script supports flags to optimize CI/CD pipelines:
|
|||||||
```
|
```
|
||||||
|
|
||||||
**Available flags:**
|
**Available flags:**
|
||||||
|
|
||||||
- `--skip-install`: Skip dependency installation
|
- `--skip-install`: Skip dependency installation
|
||||||
- `--skip-build`: Skip build steps
|
- `--skip-build`: Skip build steps
|
||||||
- `--skip-typings`: Skip typings build (use when already built)
|
- `--skip-typings`: Skip typings build (use when already built)
|
||||||
@ -225,8 +227,9 @@ The test app will be available at [http://localhost:5080](http://localhost:5080)
|
|||||||
```
|
```
|
||||||
|
|
||||||
Documentation files will be generated in:
|
Documentation files will be generated in:
|
||||||
|
|
||||||
- **Webcomponent**: `docs/webcomponent-*.md` (events, commands, attributes)
|
- **Webcomponent**: `docs/webcomponent-*.md` (events, commands, attributes)
|
||||||
- **REST API**: `backend/public/openapi/public.html`
|
- **REST API**: `meet-ce/backend/public/openapi/public.html`
|
||||||
|
|
||||||
If you specify an output directory, the documentation will be copied there.
|
If you specify an output directory, the documentation will be copied there.
|
||||||
|
|
||||||
@ -289,35 +292,41 @@ openvidu-meet/
|
|||||||
│ ├── src/
|
│ ├── src/
|
||||||
│ │ ├── api-key.ts
|
│ │ ├── api-key.ts
|
||||||
│ │ ├── auth-config.ts
|
│ │ ├── auth-config.ts
|
||||||
│ │ ├── participant.ts
|
│ │ ├── room.ts
|
||||||
│ │ ├── event.model.ts
|
|
||||||
│ │ └── ...
|
│ │ └── ...
|
||||||
│ └── package.json
|
│ └── package.json
|
||||||
│
|
│
|
||||||
├── frontend/ # Angular frontend application
|
├── frontend/ # Angular frontend application
|
||||||
│ ├── src/ # Main application source
|
│ ├── src/ # Main application source
|
||||||
│ ├── projects/
|
│ ├── projects/
|
||||||
│ │ └── shared-meet-components/ # Reusable Angular library
|
│ │ └── shared-meet-components/ # Reusable Angular library
|
||||||
│ └── webcomponent/ # Web component build
|
│ └── webcomponent/ # Web component build
|
||||||
│
|
│
|
||||||
├── backend/ # Node.js/Express backend
|
├── backend/ # Node.js/Express backend
|
||||||
│ ├── src/
|
│ ├── src/
|
||||||
|
│ │ ├── config/ # Configuration files
|
||||||
│ │ ├── controllers/ # REST API controllers
|
│ │ ├── controllers/ # REST API controllers
|
||||||
│ │ ├── services/ # Business logic
|
│ │ ├── helpers/ # Helper functions
|
||||||
│ │ ├── middleware/ # Express middleware
|
│ │ ├── middleware/ # Express middleware
|
||||||
|
│ │ ├── migrations/ # Database migration scripts
|
||||||
|
│ │ ├── models/ # Domain models
|
||||||
|
│ │ ├── repositories/ # Database interaction
|
||||||
|
│ │ ├── routes/ # API route definitions
|
||||||
|
│ │ ├── services/ # Business logic
|
||||||
|
│ │ ├── utils/ # Utility functions
|
||||||
│ │ └── environment.ts # Environment configuration
|
│ │ └── environment.ts # Environment configuration
|
||||||
│ ├── openapi/ # OpenAPI specifications
|
│ ├── openapi/ # OpenAPI specifications
|
||||||
│ └── public/ # Static files (includes built frontend)
|
│ └── public/ # Static files (includes built frontend)
|
||||||
│
|
│
|
||||||
├── testapp/ # Testing application
|
├── testapp/ # Testing application
|
||||||
│ ├── src/
|
│ ├── src/
|
||||||
│ └── public/
|
│ └── public/
|
||||||
│
|
│
|
||||||
├── docker/ # Docker build files
|
├── docker/ # Docker build files
|
||||||
│ └── create_image.sh
|
│ └── create_image.sh
|
||||||
│
|
│
|
||||||
├── docs/ # Generated documentation
|
├── docs/ # Generated documentation
|
||||||
├── scripts/ # Build and utility scripts
|
├── scripts/ # Build and utility scripts
|
||||||
└── openvidu-meet-pro/ # Professional Edition (separate license)
|
└── openvidu-meet-pro/ # Professional Edition (separate license)
|
||||||
```
|
```
|
||||||
|
|
||||||
@ -412,6 +421,7 @@ Licensed under the Apache License, Version 2.0. See [LICENSE](LICENSE) for detai
|
|||||||
|
|
||||||
- [OpenVidu Website](https://openvidu.io/)
|
- [OpenVidu Website](https://openvidu.io/)
|
||||||
- [OpenVidu Meet](https://openvidu.io/latest/meet/)
|
- [OpenVidu Meet](https://openvidu.io/latest/meet/)
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
For questions and support, visit our [community forum](https://openvidu.discourse.group/).
|
For questions and support, visit our [community forum](https://openvidu.discourse.group/).
|
||||||
|
|||||||
3
meet-ce/.vscode/settings.json
vendored
3
meet-ce/.vscode/settings.json
vendored
@ -1,8 +1,9 @@
|
|||||||
{
|
{
|
||||||
"jest.jestCommandLine": "node --experimental-vm-modules ../../node_modules/.bin/jest",
|
"jest.jestCommandLine": "node --experimental-vm-modules ../../node_modules/.bin/jest --config jest.integration.config.mjs",
|
||||||
"jest.rootPath": "backend",
|
"jest.rootPath": "backend",
|
||||||
"jest.nodeEnv": {
|
"jest.nodeEnv": {
|
||||||
"NODE_OPTIONS": "--experimental-vm-modules"
|
"NODE_OPTIONS": "--experimental-vm-modules"
|
||||||
},
|
},
|
||||||
"jest.runMode": "on-demand"
|
"jest.runMode": "on-demand"
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,6 +1,7 @@
|
|||||||
USE_HTTPS=false
|
USE_HTTPS=false
|
||||||
MEET_LOG_LEVEL=verbose
|
MEET_LOG_LEVEL=debug
|
||||||
SERVER_CORS_ORIGIN=*
|
SERVER_CORS_ORIGIN=*
|
||||||
MEET_INITIAL_API_KEY=meet-api-key
|
MEET_INITIAL_API_KEY=meet-api-key
|
||||||
MEET_INITIAL_WEBHOOK_ENABLED=true
|
MEET_INITIAL_WEBHOOK_ENABLED=true
|
||||||
MEET_INITIAL_WEBHOOK_URL=http://localhost:5080/webhook
|
MEET_INITIAL_WEBHOOK_URL=http://localhost:5080/webhook
|
||||||
|
MEETING_DEPARTURE_TIMEOUT=1s
|
||||||
@ -23,9 +23,26 @@ pnpm install
|
|||||||
pnpm run build:prod
|
pnpm run build:prod
|
||||||
```
|
```
|
||||||
|
|
||||||
## Storage Structure
|
## Storage Architecture
|
||||||
|
|
||||||
The OpenVidu Meet backend uses an S3 bucket to store all application data, including rooms, recordings, user information, and system config. The bucket follows a hierarchical structure organized as follows:
|
The OpenVidu Meet backend uses **MongoDB** as its primary data storage system for all application data, including rooms, recordings, user information, API keys, and system configuration.
|
||||||
|
|
||||||
|
### MongoDB Collections
|
||||||
|
|
||||||
|
The application manages the following MongoDB collections:
|
||||||
|
|
||||||
|
- **`meetglobalconfigs`**: System-wide configuration (singleton collection)
|
||||||
|
- **`meetusers`**: User accounts with authentication and roles
|
||||||
|
- **`meetapikeys`**: API keys for authentication
|
||||||
|
- **`meetrooms`**: Room configurations and metadata
|
||||||
|
- **`meetrecordings`**: Recording metadata and access information
|
||||||
|
- **`meetmigrations`**: Migration tracking for data and schema migrations
|
||||||
|
|
||||||
|
Each document in these collections includes a `schemaVersion` field for schema evolution tracking (internal use only, not exposed via API).
|
||||||
|
|
||||||
|
### Legacy Storage (S3/ABS/GCS)
|
||||||
|
|
||||||
|
Prior versions of OpenVidu Meet used cloud object storage (S3, Azure Blob Storage, or Google Cloud Storage) for data persistence. The legacy storage structure followed this organization:
|
||||||
|
|
||||||
### Bucket Structure
|
### Bucket Structure
|
||||||
|
|
||||||
@ -109,6 +126,45 @@ Where:
|
|||||||
|
|
||||||
This naming convention ensures uniqueness and provides traceability between the recording file, its metadata, and the originating room session.
|
This naming convention ensures uniqueness and provides traceability between the recording file, its metadata, and the originating room session.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Data Migration System
|
||||||
|
|
||||||
|
OpenVidu Meet includes a comprehensive migration system to handle data persistence changes and schema evolution.
|
||||||
|
|
||||||
|
### Legacy Storage to MongoDB Migration
|
||||||
|
|
||||||
|
On first startup, the application automatically migrates existing data from legacy storage (S3/Azure Blob Storage/Google Cloud Storage) to MongoDB. This migration:
|
||||||
|
|
||||||
|
- **Runs automatically** on application startup if legacy storage is configured
|
||||||
|
- **Is idempotent** - safe to run multiple times (skips already migrated data)
|
||||||
|
- **Preserves all data** - rooms, recordings, users, API keys, and global config
|
||||||
|
- **Tracks progress** in the `meetmigrations` collection
|
||||||
|
- **Is HA-safe** using distributed locks to prevent concurrent migrations
|
||||||
|
|
||||||
|
### MongoDB Schema Migration System
|
||||||
|
|
||||||
|
The application uses a schema versioning system to safely evolve MongoDB document structures over time. This system:
|
||||||
|
|
||||||
|
- **Runs automatically** at startup before accepting requests
|
||||||
|
- **Tracks schema versions** via the `schemaVersion` field in each document
|
||||||
|
- **Supports forward-only migrations** (v1 → v2 → v3)
|
||||||
|
- **Processes in batches** for efficiency with large collections
|
||||||
|
- **Is HA-safe** using distributed locks
|
||||||
|
- **Validates before execution** to ensure migration safety
|
||||||
|
|
||||||
|
Schema migrations handle scenarios like:
|
||||||
|
|
||||||
|
- Adding new required fields with default values
|
||||||
|
- Removing deprecated fields
|
||||||
|
- Renaming or restructuring fields
|
||||||
|
- Data type transformations
|
||||||
|
|
||||||
|
For detailed information about creating and managing schema migrations, see:
|
||||||
|
📖 **[Schema Migration Documentation](./src/migrations/README.md)**
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
## Recordings
|
## Recordings
|
||||||
|
|
||||||
The recording feature is based on the following key concepts:
|
The recording feature is based on the following key concepts:
|
||||||
@ -140,10 +196,10 @@ flowchart TD
|
|||||||
L -- "Error (recording not found, already stopped,\nor unknown error)" --> O["Reject Request"] --> J
|
L -- "Error (recording not found, already stopped,\nor unknown error)" --> O["Reject Request"] --> J
|
||||||
```
|
```
|
||||||
|
|
||||||
4. **Failure handling**:
|
3. **Failure handling**:
|
||||||
If an OpenVidu instance crashes while a recording is active, the lock remains in place. This scenario can block subsequent recording attempts if the lock is not released promptly. To mitigate this issue, a lock garbage collector is implemented to periodically clean up orphaned locks.
|
If an OpenVidu instance crashes while a recording is active, the lock remains in place. This scenario can block subsequent recording attempts if the lock is not released promptly. To mitigate this issue, a lock garbage collector is implemented to periodically clean up orphaned locks.
|
||||||
|
|
||||||
The garbage collector runs when the OpenVidu deployment starts, and then every 30 minutes.
|
The garbage collector runs when the OpenVidu deployment starts, and then every 15 minutes.
|
||||||
|
|
||||||
```mermaid
|
```mermaid
|
||||||
graph TD;
|
graph TD;
|
||||||
@ -169,46 +225,53 @@ graph TD;
|
|||||||
L --> M
|
L --> M
|
||||||
M -->|More rooms| E
|
M -->|More rooms| E
|
||||||
M -->|No more rooms| N[Process completed]
|
M -->|No more rooms| N[Process completed]
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
5. **Stale recordings cleanup**:
|
4. **Stale recordings cleanup**:
|
||||||
To handle recordings that become stale due to network issues, LiveKit or Egress crashes, or other unexpected situations, a separate cleanup process runs every 15 minutes to identify and abort recordings that haven't been updated within a configured threshold (5 minutes by default).
|
To handle recordings that become stale due to network issues, LiveKit or Egress crashes, or other unexpected situations, a separate cleanup process runs every 14 minutes to identify and abort recordings that haven't been updated within a configured threshold (5 minutes by default).
|
||||||
|
|
||||||
```mermaid
|
```mermaid
|
||||||
graph TD;
|
graph TD;
|
||||||
A[Initiate stale recordings cleanup] --> B[Get all in-progress recordings from LiveKit]
|
A[Initiate stale recordings cleanup] --> B[Get all active recordings from database<br/>ACTIVE or ENDING status]
|
||||||
B -->|Error| C[Log error and exit]
|
B -->|Error| C[Log error and exit]
|
||||||
B -->|No recordings found| D[Log and exit]
|
B -->|No recordings found| D[Log and exit]
|
||||||
B -->|Recordings found| E[Process recordings in batches of 10]
|
B -->|Recordings found| E[Process recordings in batches of 10]
|
||||||
|
|
||||||
E --> F[For each recording in batch]
|
E --> F[For each recording in batch]
|
||||||
F --> G[Extract recording ID and updatedAt]
|
F --> G[Extract recordingId, roomId and egressId]
|
||||||
G --> H[Get recording status from storage]
|
G --> H[Check for corresponding egress in LiveKit]
|
||||||
|
|
||||||
H -->|Recording already ABORTED| I[Mark as already processed]
|
H -->|No egress found| I[Recording is stale - no egress exists]
|
||||||
H -->|Recording active| J[Check if updatedAt exists]
|
H -->|Egress exists| J[Extract updatedAt from egress]
|
||||||
|
|
||||||
J -->|No updatedAt timestamp| K[Keep as fresh - log warning]
|
I --> K[Update status to ABORTED in database]
|
||||||
J -->|Has updatedAt| L[Calculate if stale]
|
K --> L[Log successful abort - no egress found]
|
||||||
|
|
||||||
L -->|Still fresh| M[Log as fresh]
|
J -->|No updatedAt timestamp| M[Keep as fresh - log warning]
|
||||||
L -->|Is stale| N[Abort stale recording]
|
J -->|Has updatedAt| N[Check if recording age is stale]
|
||||||
|
|
||||||
N --> O[Update status to ABORTED in storage]
|
N -->|Age not stale| O[Log as fresh]
|
||||||
N --> P[Stop egress in LiveKit]
|
N -->|Age is stale| P[Check room existence]
|
||||||
O --> Q[Log successful abort]
|
|
||||||
P --> Q
|
|
||||||
|
|
||||||
I --> R[Continue to next recording]
|
P -->|Room does not exist| Q[Mark as stale]
|
||||||
K --> R
|
P -->|Room exists| R[Check if room has participants]
|
||||||
M --> R
|
|
||||||
Q --> R
|
|
||||||
|
|
||||||
R -->|More recordings in batch| F
|
R -->|No participants| Q
|
||||||
R -->|Batch complete| S[Process next batch]
|
R -->|Has participants| O
|
||||||
S -->|More batches| E
|
|
||||||
S -->|All batches processed| T[Log completion metrics]
|
|
||||||
T --> U[Process completed]
|
|
||||||
|
|
||||||
|
Q --> S[Update status to ABORTED in database]
|
||||||
|
Q --> T[Stop egress in LiveKit]
|
||||||
|
S --> U[Log successful abort]
|
||||||
|
T --> U
|
||||||
|
|
||||||
|
L --> V[Continue to next recording]
|
||||||
|
M --> V
|
||||||
|
O --> V
|
||||||
|
U --> V
|
||||||
|
|
||||||
|
V -->|More recordings in batch| F
|
||||||
|
V -->|Batch complete| W[Process next batch]
|
||||||
|
W -->|More batches| E
|
||||||
|
W -->|All batches processed| X[Log completion metrics]
|
||||||
|
X --> Y[Process completed]
|
||||||
```
|
```
|
||||||
|
|||||||
@ -1,9 +1,15 @@
|
|||||||
export * from './src/routes/index.js';
|
// Main entry point for @openvidu-meet/backend package
|
||||||
export * from './src/controllers/index.js';
|
export * from './src/config/internal-config.js';
|
||||||
export * from './src/services/index.js';
|
|
||||||
export * from './src/models/index.js';
|
|
||||||
export * from './src/helpers/index.js';
|
|
||||||
export * from './src/environment.js';
|
export * from './src/environment.js';
|
||||||
|
export * from './src/server.js';
|
||||||
|
|
||||||
|
// Export other modules as needed
|
||||||
export * from './src/config/index.js';
|
export * from './src/config/index.js';
|
||||||
|
export * from './src/controllers/index.js';
|
||||||
|
export * from './src/helpers/index.js';
|
||||||
export * from './src/middlewares/index.js';
|
export * from './src/middlewares/index.js';
|
||||||
|
export * from './src/models/index.js';
|
||||||
|
export * from './src/routes/index.js';
|
||||||
|
export * from './src/services/index.js';
|
||||||
export * from './src/utils/index.js';
|
export * from './src/utils/index.js';
|
||||||
|
|
||||||
|
|||||||
@ -15,16 +15,19 @@ const jestConfig = {
|
|||||||
'^(\\.{1,2}/.*)\\.js$': '$1' // Allow importing js files and resolving to ts files
|
'^(\\.{1,2}/.*)\\.js$': '$1' // Allow importing js files and resolving to ts files
|
||||||
},
|
},
|
||||||
transform: {
|
transform: {
|
||||||
'^.+\\.tsx?$': ['ts-jest', {
|
'^.+\\.tsx?$': [
|
||||||
tsconfig: {
|
'ts-jest',
|
||||||
module: 'esnext',
|
{
|
||||||
moduleResolution: 'node16',
|
tsconfig: {
|
||||||
esModuleInterop: true,
|
module: 'esnext',
|
||||||
allowSyntheticDefaultImports: true,
|
moduleResolution: 'node16',
|
||||||
isolatedModules: true
|
esModuleInterop: true,
|
||||||
},
|
allowSyntheticDefaultImports: true,
|
||||||
useESM: true
|
isolatedModules: true
|
||||||
}]
|
},
|
||||||
|
useESM: true
|
||||||
|
}
|
||||||
|
]
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
12
meet-ce/backend/jest.integration.config.mjs
Normal file
12
meet-ce/backend/jest.integration.config.mjs
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
import baseConfig from './jest.config.mjs';
|
||||||
|
|
||||||
|
const integrationConfig = {
|
||||||
|
...baseConfig,
|
||||||
|
|
||||||
|
runInBand: true,
|
||||||
|
forceExit: true,
|
||||||
|
detectOpenHandles: true,
|
||||||
|
testMatch: ['**/tests/integration/**/*.(spec|test).ts'],
|
||||||
|
};
|
||||||
|
|
||||||
|
export default integrationConfig;
|
||||||
@ -1,6 +0,0 @@
|
|||||||
description: >
|
|
||||||
The cookie containing the access token.
|
|
||||||
This cookie is used to authenticate the user in subsequent requests.
|
|
||||||
schema:
|
|
||||||
type: string
|
|
||||||
example: 'OvMeetAccessToken=token_123456; Path=/; HttpOnly; SameSite=Strict'
|
|
||||||
@ -1,6 +0,0 @@
|
|||||||
description: >
|
|
||||||
The cookie containing the participant token.
|
|
||||||
This cookie is used to authenticate the participant in the room.
|
|
||||||
schema:
|
|
||||||
type: string
|
|
||||||
example: 'OvMeetParticipantToken=token_123456; Path=/; HttpOnly; SameSite=Strict'
|
|
||||||
@ -1,6 +0,0 @@
|
|||||||
description: >
|
|
||||||
The cookie containing the recording token.
|
|
||||||
This cookie is used to access the recordings in a room.
|
|
||||||
schema:
|
|
||||||
type: string
|
|
||||||
example: 'OvMeetRecordingToken=token_123456; Path=/; HttpOnly; SameSite=Strict'
|
|
||||||
@ -1,6 +0,0 @@
|
|||||||
description: >
|
|
||||||
The cookie containing the refresh token.
|
|
||||||
This cookie is used to refresh the access token when it expires.
|
|
||||||
schema:
|
|
||||||
type: string
|
|
||||||
example: 'OvMeetRefreshToken=token_123456; Path=/meet/internal-api/v1/auth; HttpOnly; SameSite=Strict'
|
|
||||||
@ -1,7 +0,0 @@
|
|||||||
name: participantIdentity
|
|
||||||
in: path
|
|
||||||
required: true
|
|
||||||
description: The identity of the participant.
|
|
||||||
schema:
|
|
||||||
type: string
|
|
||||||
example: 'Alice'
|
|
||||||
@ -1,6 +1,6 @@
|
|||||||
name: secret
|
name: secret
|
||||||
in: path
|
in: path
|
||||||
required: true
|
required: true
|
||||||
description: The secret value from the room URL used to connect to the room.
|
description: The secret value from the room URL used to access the room.
|
||||||
schema:
|
schema:
|
||||||
type: string
|
type: string
|
||||||
|
|||||||
@ -1,12 +0,0 @@
|
|||||||
name: x-participant-role
|
|
||||||
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.
|
|
||||||
- `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', 'speaker']
|
|
||||||
@ -1,12 +1,7 @@
|
|||||||
name: status
|
name: status
|
||||||
in: query
|
in: query
|
||||||
required: false
|
required: false
|
||||||
description: |
|
description: Filter recordings by their status.
|
||||||
Filter recordings by their status.
|
|
||||||
|
|
||||||
You can provide multiple statuses as a comma-separated list (e.g., `status=active,failed`).
|
|
||||||
|
|
||||||
> ⚠️ **Note:** Using this filter may impact performance for large datasets.
|
|
||||||
schema:
|
schema:
|
||||||
type: string
|
type: string
|
||||||
enum:
|
enum:
|
||||||
|
|||||||
@ -2,7 +2,8 @@ name: roomName
|
|||||||
in: query
|
in: query
|
||||||
required: false
|
required: false
|
||||||
description: >
|
description: >
|
||||||
The name of the room.
|
Filter rooms by name. The search is case-insensitive and matches rooms that contain the specified text.
|
||||||
|
For example, 'room' will match 'MyRoom', 'room123', and 'Conference Room'.
|
||||||
schema:
|
schema:
|
||||||
type: string
|
type: string
|
||||||
example: 'room'
|
example: 'room'
|
||||||
|
|||||||
@ -0,0 +1,10 @@
|
|||||||
|
name: status
|
||||||
|
in: query
|
||||||
|
required: false
|
||||||
|
description: Filter rooms by their status.
|
||||||
|
schema:
|
||||||
|
type: string
|
||||||
|
enum:
|
||||||
|
- open
|
||||||
|
- active_meeting
|
||||||
|
- closed
|
||||||
@ -0,0 +1,6 @@
|
|||||||
|
name: sortField
|
||||||
|
in: query
|
||||||
|
required: false
|
||||||
|
description: The field by which to sort the results.
|
||||||
|
schema:
|
||||||
|
type: string
|
||||||
@ -0,0 +1,9 @@
|
|||||||
|
name: sortOrder
|
||||||
|
in: query
|
||||||
|
required: false
|
||||||
|
description: The order in which to sort the results. Use "asc" for ascending order and "desc" for descending order.
|
||||||
|
schema:
|
||||||
|
type: string
|
||||||
|
enum:
|
||||||
|
- asc
|
||||||
|
- desc
|
||||||
@ -0,0 +1,6 @@
|
|||||||
|
description: Create AI assistant activation request
|
||||||
|
required: true
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
$ref: '../../schemas/internal/ai-assistant-create-request.yaml'
|
||||||
@ -1,6 +0,0 @@
|
|||||||
description: Participant details
|
|
||||||
required: true
|
|
||||||
content:
|
|
||||||
application/json:
|
|
||||||
schema:
|
|
||||||
$ref: '../../schemas/internal/meet-participant-options.yaml'
|
|
||||||
@ -0,0 +1,6 @@
|
|||||||
|
description: Room member token options
|
||||||
|
required: true
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
$ref: '../../schemas/internal/room-member-token-options.yaml'
|
||||||
@ -1,11 +0,0 @@
|
|||||||
description: Room to record
|
|
||||||
required: true
|
|
||||||
content:
|
|
||||||
application/json:
|
|
||||||
schema:
|
|
||||||
type: object
|
|
||||||
properties:
|
|
||||||
roomId:
|
|
||||||
type: string
|
|
||||||
description: The unique identifier of the room to record.
|
|
||||||
example: 'room-123'
|
|
||||||
@ -0,0 +1,35 @@
|
|||||||
|
description: Room to record
|
||||||
|
required: true
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
type: object
|
||||||
|
properties:
|
||||||
|
roomId:
|
||||||
|
type: string
|
||||||
|
description: The unique identifier of the room to record.
|
||||||
|
example: 'room-123'
|
||||||
|
config:
|
||||||
|
type: object
|
||||||
|
description: |
|
||||||
|
Optional configuration to override the room's recording configuration for this specific recording.
|
||||||
|
If not provided, the recording will use the configuration defined in the room's config.
|
||||||
|
properties:
|
||||||
|
layout:
|
||||||
|
type: string
|
||||||
|
enum:
|
||||||
|
- grid
|
||||||
|
- speaker
|
||||||
|
- single-speaker
|
||||||
|
example: speaker
|
||||||
|
description: |
|
||||||
|
Defines the layout of the recording. This will override the room's default recording layout.
|
||||||
|
Options are:
|
||||||
|
- `grid`: All participants are shown in a grid layout.
|
||||||
|
- `speaker`: The active speaker is shown prominently, with other participants in smaller thumbnails.
|
||||||
|
- `single-speaker`: Only the active speaker is shown in the recording.
|
||||||
|
encoding:
|
||||||
|
description: Defines the encoding settings for the recording. This will override the room's default recording encoding.
|
||||||
|
oneOf:
|
||||||
|
- $ref: '../schemas/meet-room-config.yaml#/MeetRecordingEncodingPreset'
|
||||||
|
- $ref: '../schemas/meet-room-config.yaml#/MeetRecordingEncodingOptions'
|
||||||
@ -11,6 +11,7 @@ content:
|
|||||||
chat:
|
chat:
|
||||||
enabled: true
|
enabled: true
|
||||||
recording:
|
recording:
|
||||||
enabled: false
|
enabled: true
|
||||||
|
encoding: H264_720P_30
|
||||||
virtualBackground:
|
virtualBackground:
|
||||||
enabled: true
|
enabled: true
|
||||||
|
|||||||
@ -0,0 +1,17 @@
|
|||||||
|
description: New room status
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
type: object
|
||||||
|
properties:
|
||||||
|
status:
|
||||||
|
type: string
|
||||||
|
enum:
|
||||||
|
- open
|
||||||
|
- active_meeting
|
||||||
|
- closed
|
||||||
|
example: closed
|
||||||
|
description: |
|
||||||
|
The new status of the room. Options are:
|
||||||
|
- open: The room will be open for new participants to join.
|
||||||
|
- closed: The room will be closed to new participants.
|
||||||
@ -2,7 +2,7 @@ description: Conflict — The recording cannot be started due to resource state
|
|||||||
content:
|
content:
|
||||||
application/json:
|
application/json:
|
||||||
schema:
|
schema:
|
||||||
$ref: '../../schemas/error.yaml'
|
$ref: '../schemas/error.yaml'
|
||||||
examples:
|
examples:
|
||||||
already_recording:
|
already_recording:
|
||||||
summary: Room is already being recorded
|
summary: Room is already being recorded
|
||||||
@ -2,7 +2,7 @@ description: Conflict — The recording is starting or already stopped
|
|||||||
content:
|
content:
|
||||||
application/json:
|
application/json:
|
||||||
schema:
|
schema:
|
||||||
$ref: '../../schemas/error.yaml'
|
$ref: '../schemas/error.yaml'
|
||||||
examples:
|
examples:
|
||||||
starting_recording:
|
starting_recording:
|
||||||
summary: Recording is starting
|
summary: Recording is starting
|
||||||
@ -0,0 +1,8 @@
|
|||||||
|
description: Room not found
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
$ref: '../schemas/error.yaml'
|
||||||
|
example:
|
||||||
|
error: 'Room Error'
|
||||||
|
message: 'Room "room_123" has an active meeting'
|
||||||
@ -2,7 +2,7 @@ description: Service Unavailable — The recording service is unavailable
|
|||||||
content:
|
content:
|
||||||
application/json:
|
application/json:
|
||||||
schema:
|
schema:
|
||||||
$ref: '../../schemas/error.yaml'
|
$ref: '../schemas/error.yaml'
|
||||||
examples:
|
examples:
|
||||||
starting_timeout:
|
starting_timeout:
|
||||||
summary: Recording service timed out
|
summary: Recording service timed out
|
||||||
@ -1,4 +1,4 @@
|
|||||||
description: Forbidden — Insufficient permissions
|
description: Forbidden - Insufficient Permissions
|
||||||
content:
|
content:
|
||||||
application/json:
|
application/json:
|
||||||
schema:
|
schema:
|
||||||
|
|||||||
@ -0,0 +1,16 @@
|
|||||||
|
description: Forbidden
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
$ref: '../schemas/error.yaml'
|
||||||
|
examples:
|
||||||
|
forbidden_error:
|
||||||
|
summary: Forbidden Error Example
|
||||||
|
value:
|
||||||
|
error: Authorization Error
|
||||||
|
message: 'Insufficient permissions to access this resource'
|
||||||
|
recording_not_allowed:
|
||||||
|
summary: Recording Not Allowed in Room Example
|
||||||
|
value:
|
||||||
|
error: Recording Error
|
||||||
|
message: 'Recording is disabled for room room-123'
|
||||||
@ -1,8 +0,0 @@
|
|||||||
description: Invalid participant role provided
|
|
||||||
content:
|
|
||||||
application/json:
|
|
||||||
schema:
|
|
||||||
$ref: '../../schemas/error.yaml'
|
|
||||||
example:
|
|
||||||
error: Participant Error
|
|
||||||
message: 'No valid participant role provided'
|
|
||||||
@ -1,8 +0,0 @@
|
|||||||
description: Conflict — The participant already exists in the room
|
|
||||||
content:
|
|
||||||
application/json:
|
|
||||||
schema:
|
|
||||||
$ref: '../../schemas/error.yaml'
|
|
||||||
example:
|
|
||||||
error: 'Participant Error'
|
|
||||||
message: 'Participant "Alice" already exists in room "room_123"'
|
|
||||||
@ -1,8 +0,0 @@
|
|||||||
description: Room metadata not found
|
|
||||||
content:
|
|
||||||
application/json:
|
|
||||||
schema:
|
|
||||||
$ref: '../../schemas/error.yaml'
|
|
||||||
example:
|
|
||||||
error: 'Room Error'
|
|
||||||
message: 'Room metadata for "room_123" not found. Room "room_123" does not exist or has no recordings associated'
|
|
||||||
@ -0,0 +1,5 @@
|
|||||||
|
description: Successfully created or reused AI assistant activation
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
$ref: '../../schemas/internal/ai-assistant-create-response.yaml'
|
||||||
@ -1,13 +0,0 @@
|
|||||||
description: Successfully generated the participant token
|
|
||||||
# headers:
|
|
||||||
# Set-Cookie:
|
|
||||||
# $ref: '../../headers/set-cookie-participant-token.yaml'
|
|
||||||
content:
|
|
||||||
application/json:
|
|
||||||
schema:
|
|
||||||
type: object
|
|
||||||
properties:
|
|
||||||
token:
|
|
||||||
type: string
|
|
||||||
description: >
|
|
||||||
The token to authenticate the participant.
|
|
||||||
@ -1,13 +0,0 @@
|
|||||||
description: Successfully generated the recording token
|
|
||||||
# headers:
|
|
||||||
# Set-Cookie:
|
|
||||||
# $ref: '../../headers/set-cookie-recording-token.yaml'
|
|
||||||
content:
|
|
||||||
application/json:
|
|
||||||
schema:
|
|
||||||
type: object
|
|
||||||
properties:
|
|
||||||
token:
|
|
||||||
type: string
|
|
||||||
description: >
|
|
||||||
The token to access the recordings in the specified OpenVidu Meet room.
|
|
||||||
@ -0,0 +1,10 @@
|
|||||||
|
description: Successfully generated the room member token
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
type: object
|
||||||
|
properties:
|
||||||
|
token:
|
||||||
|
type: string
|
||||||
|
description: >
|
||||||
|
The token to authenticate the user to access the room and its resources.
|
||||||
@ -0,0 +1,5 @@
|
|||||||
|
description: Analytics data retrieved successfully
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
$ref: '../../schemas/internal/meet-analytics.yaml'
|
||||||
@ -0,0 +1,5 @@
|
|||||||
|
description: Successfully retrieved captions config
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
$ref: '../../schemas/internal/global-captions-config.yaml'
|
||||||
@ -2,4 +2,4 @@ description: Successfully retrieved user profile
|
|||||||
content:
|
content:
|
||||||
application/json:
|
application/json:
|
||||||
schema:
|
schema:
|
||||||
$ref: '../../schemas/internal/user.yaml'
|
$ref: '../../schemas/internal/meet-user.yaml'
|
||||||
|
|||||||
@ -2,4 +2,4 @@ description: Successfully retrieved the room role and associated permissions
|
|||||||
content:
|
content:
|
||||||
application/json:
|
application/json:
|
||||||
schema:
|
schema:
|
||||||
$ref: '../../schemas/internal/meet-room-role-permissions.yaml'
|
$ref: '../../schemas/internal/room-member-role-permissions.yaml'
|
||||||
@ -4,7 +4,7 @@ content:
|
|||||||
schema:
|
schema:
|
||||||
type: array
|
type: array
|
||||||
items:
|
items:
|
||||||
$ref: '../../schemas/internal/meet-room-role-permissions.yaml'
|
$ref: '../../schemas/internal/room-member-role-permissions.yaml'
|
||||||
example:
|
example:
|
||||||
- role: 'moderator'
|
- role: 'moderator'
|
||||||
permissions:
|
permissions:
|
||||||
@ -17,6 +17,8 @@ content:
|
|||||||
canUpdateOwnMetadata: true
|
canUpdateOwnMetadata: true
|
||||||
openvidu:
|
openvidu:
|
||||||
canRecord: true
|
canRecord: true
|
||||||
|
canRetrieveRecordings: true
|
||||||
|
canDeleteRecordings: true
|
||||||
canChat: true
|
canChat: true
|
||||||
canChangeVirtualBackground: true
|
canChangeVirtualBackground: true
|
||||||
- role: 'speaker'
|
- role: 'speaker'
|
||||||
@ -30,5 +32,7 @@ content:
|
|||||||
canUpdateOwnMetadata: true
|
canUpdateOwnMetadata: true
|
||||||
openvidu:
|
openvidu:
|
||||||
canRecord: false
|
canRecord: false
|
||||||
|
canRetrieveRecordings: true
|
||||||
|
canDeleteRecordings: false
|
||||||
canChat: true
|
canChat: true
|
||||||
canChangeVirtualBackground: true
|
canChangeVirtualBackground: true
|
||||||
@ -6,4 +6,4 @@ content:
|
|||||||
properties:
|
properties:
|
||||||
message:
|
message:
|
||||||
type: string
|
type: string
|
||||||
example: Participant 'Alice' kicked successfully from room 'room-123'
|
example: Participant 'Alice' kicked successfully from meeting in room 'room-123'
|
||||||
|
|||||||
@ -1,7 +1,4 @@
|
|||||||
description: Successfully refreshed the access token
|
description: Successfully refreshed the access token
|
||||||
# headers:
|
|
||||||
# Set-Cookie:
|
|
||||||
# $ref: '../../headers/set-cookie-access-token.yaml'
|
|
||||||
content:
|
content:
|
||||||
application/json:
|
application/json:
|
||||||
schema:
|
schema:
|
||||||
|
|||||||
@ -1,9 +1,4 @@
|
|||||||
description: Successfully logged in
|
description: Successfully logged in
|
||||||
# headers:
|
|
||||||
# Set-Cookie:
|
|
||||||
# $ref: '../../headers/set-cookie-access-token.yaml'
|
|
||||||
# Set-Cookie*:
|
|
||||||
# $ref: '../../headers/set-cookie-refresh-token.yaml'
|
|
||||||
content:
|
content:
|
||||||
application/json:
|
application/json:
|
||||||
schema:
|
schema:
|
||||||
|
|||||||
@ -1,11 +1,4 @@
|
|||||||
description: Successfully logged out
|
description: Successfully logged out
|
||||||
# headers:
|
|
||||||
# Set-Cookie:
|
|
||||||
# description: >
|
|
||||||
# Clears the access and refresh token cookie.
|
|
||||||
# schema:
|
|
||||||
# type: string
|
|
||||||
# example: 'OvMeetAccessToken=; Path=/; HttpOnly; SameSite=Strict'
|
|
||||||
content:
|
content:
|
||||||
application/json:
|
application/json:
|
||||||
schema:
|
schema:
|
||||||
|
|||||||
@ -11,6 +11,7 @@ content:
|
|||||||
roomId: 'room-123'
|
roomId: 'room-123'
|
||||||
roomName: 'room'
|
roomName: 'room'
|
||||||
status: 'complete'
|
status: 'complete'
|
||||||
|
layout: 'grid'
|
||||||
filename: 'room-123--XX445.mp4'
|
filename: 'room-123--XX445.mp4'
|
||||||
startDate: 1600000000000
|
startDate: 1600000000000
|
||||||
endDate: 1600000003600
|
endDate: 1600000003600
|
||||||
@ -25,5 +26,6 @@ content:
|
|||||||
roomId: 'room-456'
|
roomId: 'room-456'
|
||||||
roomName: 'room'
|
roomName: 'room'
|
||||||
status: 'active'
|
status: 'active'
|
||||||
|
layout: 'grid'
|
||||||
filename: 'room-456--QR789.mp4'
|
filename: 'room-456--QR789.mp4'
|
||||||
startDate: 1682500000000
|
startDate: 1682500000000
|
||||||
|
|||||||
@ -19,6 +19,7 @@ content:
|
|||||||
roomId: 'room-123'
|
roomId: 'room-123'
|
||||||
roomName: 'room'
|
roomName: 'room'
|
||||||
status: 'active'
|
status: 'active'
|
||||||
|
layout: 'grid'
|
||||||
filename: 'room-123--XX445.mp4'
|
filename: 'room-123--XX445.mp4'
|
||||||
startDate: 1620000000000
|
startDate: 1620000000000
|
||||||
endDate: 1620000003600
|
endDate: 1620000003600
|
||||||
@ -29,6 +30,7 @@ content:
|
|||||||
roomId: 'room-456'
|
roomId: 'room-456'
|
||||||
roomName: 'room'
|
roomName: 'room'
|
||||||
status: 'complete'
|
status: 'complete'
|
||||||
|
layout: 'grid'
|
||||||
filename: 'room-456--XX678.mp4'
|
filename: 'room-456--XX678.mp4'
|
||||||
startDate: 1625000000000
|
startDate: 1625000000000
|
||||||
endDate: 1625000007200
|
endDate: 1625000007200
|
||||||
|
|||||||
@ -19,8 +19,15 @@ content:
|
|||||||
enabled: true
|
enabled: true
|
||||||
recording:
|
recording:
|
||||||
enabled: false
|
enabled: false
|
||||||
|
layout: grid
|
||||||
|
encoding: H264_720P_30
|
||||||
|
allowAccessTo: admin_moderator_speaker
|
||||||
virtualBackground:
|
virtualBackground:
|
||||||
enabled: true
|
enabled: true
|
||||||
|
e2ee:
|
||||||
|
enabled: false
|
||||||
|
captions:
|
||||||
|
enabled: true
|
||||||
moderatorUrl: 'http://localhost:6080/room/room-123?secret=123456'
|
moderatorUrl: 'http://localhost:6080/room/room-123?secret=123456'
|
||||||
speakerUrl: 'http://localhost:6080/room/room-123?secret=654321'
|
speakerUrl: 'http://localhost:6080/room/room-123?secret=654321'
|
||||||
status: open
|
status: open
|
||||||
@ -43,8 +50,21 @@ content:
|
|||||||
enabled: true
|
enabled: true
|
||||||
recording:
|
recording:
|
||||||
enabled: false
|
enabled: false
|
||||||
|
layout: grid
|
||||||
|
encoding:
|
||||||
|
video:
|
||||||
|
width: 1920
|
||||||
|
height: 1080
|
||||||
|
framerate: 30
|
||||||
|
codec: H264_MAIN
|
||||||
|
audio:
|
||||||
|
codec: OPUS
|
||||||
|
bitrate: 128
|
||||||
|
allowAccessTo: admin_moderator_speaker
|
||||||
virtualBackground:
|
virtualBackground:
|
||||||
enabled: true
|
enabled: true
|
||||||
|
e2ee:
|
||||||
|
enabled: false
|
||||||
|
|
||||||
fields=moderatorUrl,speakerUrl:
|
fields=moderatorUrl,speakerUrl:
|
||||||
summary: Response containing only moderator and speaker URLs
|
summary: Response containing only moderator and speaker URLs
|
||||||
|
|||||||
@ -28,8 +28,15 @@ content:
|
|||||||
enabled: true
|
enabled: true
|
||||||
recording:
|
recording:
|
||||||
enabled: false
|
enabled: false
|
||||||
|
layout: grid
|
||||||
|
encoding: H264_720P_30
|
||||||
|
allowAccessTo: admin_moderator_speaker
|
||||||
virtualBackground:
|
virtualBackground:
|
||||||
enabled: true
|
enabled: true
|
||||||
|
e2ee:
|
||||||
|
enabled: false
|
||||||
|
captions:
|
||||||
|
enabled: true
|
||||||
moderatorUrl: 'http://localhost:6080/room/room-123?secret=123456'
|
moderatorUrl: 'http://localhost:6080/room/room-123?secret=123456'
|
||||||
speakerUrl: 'http://localhost:6080/room/room-123?secret=654321'
|
speakerUrl: 'http://localhost:6080/room/room-123?secret=654321'
|
||||||
status: open
|
status: open
|
||||||
@ -46,8 +53,21 @@ content:
|
|||||||
enabled: false
|
enabled: false
|
||||||
recording:
|
recording:
|
||||||
enabled: true
|
enabled: true
|
||||||
|
layout: grid
|
||||||
|
encoding:
|
||||||
|
video:
|
||||||
|
width: 1280
|
||||||
|
height: 720
|
||||||
|
framerate: 60
|
||||||
|
codec: H264_HIGH
|
||||||
|
audio:
|
||||||
|
codec: AAC
|
||||||
|
bitrate: 192
|
||||||
|
allowAccessTo: admin_moderator_speaker
|
||||||
virtualBackground:
|
virtualBackground:
|
||||||
enabled: false
|
enabled: false
|
||||||
|
e2ee:
|
||||||
|
enabled: false
|
||||||
moderatorUrl: 'http://localhost:6080/room/room-456?secret=789012'
|
moderatorUrl: 'http://localhost:6080/room/room-456?secret=789012'
|
||||||
speakerUrl: 'http://localhost:6080/room/room-456?secret=210987'
|
speakerUrl: 'http://localhost:6080/room/room-456?secret=210987'
|
||||||
status: open
|
status: open
|
||||||
@ -80,6 +100,8 @@ content:
|
|||||||
enabled: false
|
enabled: false
|
||||||
virtualBackground:
|
virtualBackground:
|
||||||
enabled: true
|
enabled: true
|
||||||
|
e2ee:
|
||||||
|
enabled: false
|
||||||
- roomId: 'room-456'
|
- roomId: 'room-456'
|
||||||
roomName: 'room'
|
roomName: 'room'
|
||||||
creationDate: 1620001000000
|
creationDate: 1620001000000
|
||||||
@ -91,6 +113,8 @@ content:
|
|||||||
enabled: true
|
enabled: true
|
||||||
virtualBackground:
|
virtualBackground:
|
||||||
enabled: false
|
enabled: false
|
||||||
|
e2ee:
|
||||||
|
enabled: false
|
||||||
pagination:
|
pagination:
|
||||||
isTruncated: true
|
isTruncated: true
|
||||||
nextPageToken: 'abc123'
|
nextPageToken: 'abc123'
|
||||||
|
|||||||
@ -0,0 +1,10 @@
|
|||||||
|
description: Success response for scheduling room closure
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
type: object
|
||||||
|
properties:
|
||||||
|
message:
|
||||||
|
type: string
|
||||||
|
example:
|
||||||
|
message: Room 'room-123' scheduled to be closed when the meeting ends
|
||||||
@ -1,12 +0,0 @@
|
|||||||
description: >
|
|
||||||
All specified rooms were marked for deletion (due to active participants)
|
|
||||||
and will be removed once all participants leave.
|
|
||||||
content:
|
|
||||||
application/json:
|
|
||||||
schema:
|
|
||||||
type: object
|
|
||||||
properties:
|
|
||||||
message:
|
|
||||||
type: string
|
|
||||||
example:
|
|
||||||
message: Rooms 'room-123, room-456' marked for deletion
|
|
||||||
@ -2,12 +2,13 @@ description: Successfully created the OpenVidu Meet recording
|
|||||||
content:
|
content:
|
||||||
application/json:
|
application/json:
|
||||||
schema:
|
schema:
|
||||||
$ref: '../../schemas/meet-recording.yaml'
|
$ref: '../schemas/meet-recording.yaml'
|
||||||
example:
|
example:
|
||||||
recordingId: 'room-123--EG_XYZ--XX445'
|
recordingId: 'room-123--EG_XYZ--XX445'
|
||||||
roomId: 'room-123'
|
roomId: 'room-123'
|
||||||
roomName: 'room'
|
roomName: 'room'
|
||||||
status: 'active'
|
status: 'active'
|
||||||
|
layout: 'speaker'
|
||||||
filename: 'room-123--XX445.mp4'
|
filename: 'room-123--XX445.mp4'
|
||||||
startDate: 1600000000000
|
startDate: 1600000000000
|
||||||
headers:
|
headers:
|
||||||
@ -8,12 +8,13 @@ headers:
|
|||||||
content:
|
content:
|
||||||
application/json:
|
application/json:
|
||||||
schema:
|
schema:
|
||||||
$ref: '../../schemas/meet-recording.yaml'
|
$ref: '../schemas/meet-recording.yaml'
|
||||||
example:
|
example:
|
||||||
recordingId: 'room-123--EG_XYZ--XX445'
|
recordingId: 'room-123--EG_XYZ--XX445'
|
||||||
roomId: 'room-123'
|
roomId: 'room-123'
|
||||||
roomName: 'room'
|
roomName: 'room'
|
||||||
status: 'ending'
|
status: 'ending'
|
||||||
|
layout: 'speaker'
|
||||||
filename: 'room-123--XX445.mp4'
|
filename: 'room-123--XX445.mp4'
|
||||||
startDate: 1600000000000
|
startDate: 1600000000000
|
||||||
details: 'End reason: StopEgress API'
|
details: 'End reason: StopEgress API'
|
||||||
@ -0,0 +1,10 @@
|
|||||||
|
description: Success response for updating room status
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
type: object
|
||||||
|
properties:
|
||||||
|
message:
|
||||||
|
type: string
|
||||||
|
example:
|
||||||
|
message: Room 'room-123' closed successfully
|
||||||
@ -0,0 +1,37 @@
|
|||||||
|
type: object
|
||||||
|
required:
|
||||||
|
# - scope
|
||||||
|
- capabilities
|
||||||
|
properties:
|
||||||
|
# scope:
|
||||||
|
# type: object
|
||||||
|
# required:
|
||||||
|
# - resourceType
|
||||||
|
# - resourceIds
|
||||||
|
# properties:
|
||||||
|
# resourceType:
|
||||||
|
# type: string
|
||||||
|
# enum: ['meeting']
|
||||||
|
# description: Scope resource type where assistant will be activated.
|
||||||
|
# example: meeting
|
||||||
|
# resourceIds:
|
||||||
|
# type: array
|
||||||
|
# minItems: 1
|
||||||
|
# items:
|
||||||
|
# type: string
|
||||||
|
# minLength: 1
|
||||||
|
# description: List of target resource ids.
|
||||||
|
# example: ['meeting_123']
|
||||||
|
capabilities:
|
||||||
|
type: array
|
||||||
|
minItems: 1
|
||||||
|
items:
|
||||||
|
type: object
|
||||||
|
required:
|
||||||
|
- name
|
||||||
|
properties:
|
||||||
|
name:
|
||||||
|
type: string
|
||||||
|
enum: ['live_captions']
|
||||||
|
description: AI capability to activate.
|
||||||
|
example: live_captions
|
||||||
@ -0,0 +1,14 @@
|
|||||||
|
type: object
|
||||||
|
required:
|
||||||
|
- id
|
||||||
|
- status
|
||||||
|
properties:
|
||||||
|
id:
|
||||||
|
type: string
|
||||||
|
description: Identifier of the assistant activation.
|
||||||
|
example: asst_123
|
||||||
|
status:
|
||||||
|
type: string
|
||||||
|
enum: ['active']
|
||||||
|
description: Current assistant activation state.
|
||||||
|
example: active
|
||||||
@ -0,0 +1,8 @@
|
|||||||
|
type: object
|
||||||
|
properties:
|
||||||
|
enabled:
|
||||||
|
type: boolean
|
||||||
|
description: Indicates whether captions are enabled in the system
|
||||||
|
example: true
|
||||||
|
required:
|
||||||
|
- enabled
|
||||||
@ -0,0 +1,24 @@
|
|||||||
|
type: object
|
||||||
|
description: Usage analytics data for OpenVidu Meet
|
||||||
|
properties:
|
||||||
|
totalRooms:
|
||||||
|
type: integer
|
||||||
|
description: Total number of rooms created
|
||||||
|
example: 42
|
||||||
|
activeRooms:
|
||||||
|
type: integer
|
||||||
|
description: Number of rooms currently with an active meeting
|
||||||
|
example: 3
|
||||||
|
totalRecordings:
|
||||||
|
type: integer
|
||||||
|
description: Total number of recordings created
|
||||||
|
example: 128
|
||||||
|
completeRecordings:
|
||||||
|
type: integer
|
||||||
|
description: Number of recordings that are complete and playable
|
||||||
|
example: 125
|
||||||
|
required:
|
||||||
|
- totalRooms
|
||||||
|
- activeRooms
|
||||||
|
- totalRecordings
|
||||||
|
- completeRecordings
|
||||||
@ -1,21 +0,0 @@
|
|||||||
type: object
|
|
||||||
required:
|
|
||||||
- roomId
|
|
||||||
- secret
|
|
||||||
properties:
|
|
||||||
roomId:
|
|
||||||
type: string
|
|
||||||
description: The unique identifier of the room where the participant will join.
|
|
||||||
example: 'room-123'
|
|
||||||
secret:
|
|
||||||
type: string
|
|
||||||
description: The secret token from the room Url
|
|
||||||
example: 'abc123456'
|
|
||||||
participantName:
|
|
||||||
type: string
|
|
||||||
description: The name of the participant.
|
|
||||||
example: 'Alice'
|
|
||||||
participantIdentity:
|
|
||||||
type: string
|
|
||||||
description: The unique identity of the participant.
|
|
||||||
example: 'Alice'
|
|
||||||
@ -4,10 +4,10 @@ properties:
|
|||||||
type: string
|
type: string
|
||||||
enum: ['moderator', 'speaker']
|
enum: ['moderator', 'speaker']
|
||||||
description: |
|
description: |
|
||||||
A role that a participant can have in a room.
|
A role that a user can have as a member of a room.
|
||||||
The role determines the permissions of the participant in the room.
|
The role determines the permissions of the user in the room.
|
||||||
- `moderator`: Can manage the room and its participants.
|
- `moderator`: Can manage the room resources and meeting participants.
|
||||||
- `speaker`: Can publish media streams to the room.
|
- `speaker`: Can publish media streams to the meeting.
|
||||||
example: 'moderator'
|
example: 'moderator'
|
||||||
permissions:
|
permissions:
|
||||||
type: object
|
type: object
|
||||||
@ -50,15 +50,25 @@ properties:
|
|||||||
canRecord:
|
canRecord:
|
||||||
type: boolean
|
type: boolean
|
||||||
description: >
|
description: >
|
||||||
Indicates whether the participant can record the room.
|
Indicates whether the user can record a meeting in the room.
|
||||||
|
example: true
|
||||||
|
canRetrieveRecordings:
|
||||||
|
type: boolean
|
||||||
|
description: >
|
||||||
|
Indicates whether the user can retrieve and play recordings of meetings in the room.
|
||||||
|
example: true
|
||||||
|
canDeleteRecordings:
|
||||||
|
type: boolean
|
||||||
|
description: >
|
||||||
|
Indicates whether the user can delete recordings of meetings in the room.
|
||||||
example: true
|
example: true
|
||||||
canChat:
|
canChat:
|
||||||
type: boolean
|
type: boolean
|
||||||
description: >
|
description: >
|
||||||
Indicates whether the participant can send and receive chat messages in the room.
|
Indicates whether the user can send and receive chat messages in the room.
|
||||||
example: true
|
example: true
|
||||||
canChangeVirtualBackground:
|
canChangeVirtualBackground:
|
||||||
type: boolean
|
type: boolean
|
||||||
description: >
|
description: >
|
||||||
Indicates whether the participant can change their own virtual background.
|
Indicates whether the user can change their own virtual background.
|
||||||
example: true
|
example: true
|
||||||
@ -0,0 +1,20 @@
|
|||||||
|
type: object
|
||||||
|
required:
|
||||||
|
- secret
|
||||||
|
properties:
|
||||||
|
secret:
|
||||||
|
type: string
|
||||||
|
description: A secret key for room access. Determines the member's role.
|
||||||
|
example: 'abc123456'
|
||||||
|
grantJoinMeetingPermission:
|
||||||
|
type: boolean
|
||||||
|
description: Whether to grant permission to join the meeting. If true, participantName must be provided.
|
||||||
|
example: true
|
||||||
|
participantName:
|
||||||
|
type: string
|
||||||
|
description: The name of the participant when joining the meeting. Required if `grantJoinMeetingPermission` is true and this is a new token (not a refresh).
|
||||||
|
example: 'Alice'
|
||||||
|
participantIdentity:
|
||||||
|
type: string
|
||||||
|
description: The identity of the participant in the meeting. Required when refreshing an existing token with meeting permissions.
|
||||||
|
example: 'Alice'
|
||||||
@ -26,6 +26,10 @@ MeetRoomTheme:
|
|||||||
minLength: 1
|
minLength: 1
|
||||||
maxLength: 50
|
maxLength: 50
|
||||||
example: "Default Theme"
|
example: "Default Theme"
|
||||||
|
enabled:
|
||||||
|
type: boolean
|
||||||
|
description: Whether the theme is enabled
|
||||||
|
example: true
|
||||||
baseTheme:
|
baseTheme:
|
||||||
$ref: '#/MeetRoomThemeMode'
|
$ref: '#/MeetRoomThemeMode'
|
||||||
description: Base theme mode (light or dark)
|
description: Base theme mode (light or dark)
|
||||||
@ -51,6 +55,7 @@ MeetRoomTheme:
|
|||||||
example: "#CCCCCC"
|
example: "#CCCCCC"
|
||||||
required:
|
required:
|
||||||
- name
|
- name
|
||||||
|
- enabled
|
||||||
- baseTheme
|
- baseTheme
|
||||||
|
|
||||||
MeetRoomThemeMode:
|
MeetRoomThemeMode:
|
||||||
|
|||||||
@ -22,6 +22,58 @@ properties:
|
|||||||
enum: ['starting', 'active', 'ending', 'complete', 'failed', 'aborted', 'limit_reached']
|
enum: ['starting', 'active', 'ending', 'complete', 'failed', 'aborted', 'limit_reached']
|
||||||
example: 'active'
|
example: 'active'
|
||||||
description: The status of the recording.
|
description: The status of the recording.
|
||||||
|
layout:
|
||||||
|
type: string
|
||||||
|
example: 'grid'
|
||||||
|
description: The layout of the recording.
|
||||||
|
encoding:
|
||||||
|
oneOf:
|
||||||
|
- type: string
|
||||||
|
enum: ['H264_720P_30', 'H264_720P_60', 'H264_1080P_30', 'H264_1080P_60', 'PORTRAIT_H264_720P_30', 'PORTRAIT_H264_720P_60', 'PORTRAIT_H264_1080P_30', 'PORTRAIT_H264_1080P_60']
|
||||||
|
description: Encoding preset
|
||||||
|
- type: object
|
||||||
|
properties:
|
||||||
|
video:
|
||||||
|
type: object
|
||||||
|
properties:
|
||||||
|
width:
|
||||||
|
type: integer
|
||||||
|
example: 1920
|
||||||
|
height:
|
||||||
|
type: integer
|
||||||
|
example: 1080
|
||||||
|
framerate:
|
||||||
|
type: integer
|
||||||
|
example: 30
|
||||||
|
codec:
|
||||||
|
type: string
|
||||||
|
enum: ['DEFAULT_VC', 'H264_BASELINE', 'H264_MAIN', 'H264_HIGH', 'VP8']
|
||||||
|
bitrate:
|
||||||
|
type: integer
|
||||||
|
example: 4500
|
||||||
|
keyFrameInterval:
|
||||||
|
type: number
|
||||||
|
example: 2
|
||||||
|
depth:
|
||||||
|
type: integer
|
||||||
|
example: 24
|
||||||
|
audio:
|
||||||
|
type: object
|
||||||
|
properties:
|
||||||
|
codec:
|
||||||
|
type: string
|
||||||
|
enum: ['DEFAULT_AC', 'OPUS', 'AAC', 'AC_MP3']
|
||||||
|
bitrate:
|
||||||
|
type: integer
|
||||||
|
example: 128
|
||||||
|
frequency:
|
||||||
|
type: integer
|
||||||
|
example: 48000
|
||||||
|
description: Advanced encoding options
|
||||||
|
description: |
|
||||||
|
The encoding configuration used for this recording.
|
||||||
|
Can be either a preset string or advanced encoding options.
|
||||||
|
example: 'H264_720P_30'
|
||||||
filename:
|
filename:
|
||||||
type: string
|
type: string
|
||||||
example: 'room-123--XX445.mp4'
|
example: 'room-123--XX445.mp4'
|
||||||
|
|||||||
@ -10,6 +10,12 @@ MeetRoomConfig:
|
|||||||
virtualBackground:
|
virtualBackground:
|
||||||
$ref: '#/MeetVirtualBackgroundConfig'
|
$ref: '#/MeetVirtualBackgroundConfig'
|
||||||
description: Config for virtual background in the room.
|
description: Config for virtual background in the room.
|
||||||
|
e2ee:
|
||||||
|
$ref: '#/MeetE2EEConfig'
|
||||||
|
description: Config for End-to-End Encryption (E2EE) in the room.
|
||||||
|
captions:
|
||||||
|
$ref: '#/MeetCaptionsConfig'
|
||||||
|
description: Config for live captions in the room.
|
||||||
MeetChatConfig:
|
MeetChatConfig:
|
||||||
type: object
|
type: object
|
||||||
properties:
|
properties:
|
||||||
@ -26,6 +32,29 @@ MeetRecordingConfig:
|
|||||||
default: true
|
default: true
|
||||||
example: true
|
example: true
|
||||||
description: If true, the room will be allowed to record the video of the participants.
|
description: If true, the room will be allowed to record the video of the participants.
|
||||||
|
layout:
|
||||||
|
type: string
|
||||||
|
enum:
|
||||||
|
- grid
|
||||||
|
- speaker
|
||||||
|
- single-speaker
|
||||||
|
# - grid-light
|
||||||
|
# - speaker-light
|
||||||
|
# - single-speaker-light
|
||||||
|
default: grid
|
||||||
|
example: grid
|
||||||
|
description: |
|
||||||
|
Defines the layout of the recording. Options are:
|
||||||
|
- `grid`: All participants are shown in a grid layout.
|
||||||
|
- `speaker`: The active speaker is shown prominently, with other participants in smaller thumbnails.
|
||||||
|
- `single-speaker`: Only the active speaker is shown in the recording.
|
||||||
|
# - `grid-light`: Similar to `grid` but with a light-themed background.
|
||||||
|
# - `speaker-light`: Similar to `speaker` but with a light-themed background.
|
||||||
|
# - `single-speaker-light`: Similar to `single-speaker` but with a light-themed background.
|
||||||
|
encoding:
|
||||||
|
oneOf:
|
||||||
|
- $ref: '#/MeetRecordingEncodingPreset'
|
||||||
|
- $ref: '#/MeetRecordingEncodingOptions'
|
||||||
allowAccessTo:
|
allowAccessTo:
|
||||||
type: string
|
type: string
|
||||||
enum:
|
enum:
|
||||||
@ -47,3 +76,184 @@ MeetVirtualBackgroundConfig:
|
|||||||
default: true
|
default: true
|
||||||
example: true
|
example: true
|
||||||
description: If true, the room will be allowed to use virtual background.
|
description: If true, the room will be allowed to use virtual background.
|
||||||
|
MeetE2EEConfig:
|
||||||
|
type: object
|
||||||
|
properties:
|
||||||
|
enabled:
|
||||||
|
type: boolean
|
||||||
|
default: false
|
||||||
|
example: false
|
||||||
|
description: >
|
||||||
|
If true, the room will have End-to-End Encryption (E2EE) enabled.<br/>
|
||||||
|
This ensures that the media streams are encrypted from the sender to the receiver, providing enhanced privacy and security for the participants.<br/>
|
||||||
|
**Enabling E2EE will disable the recording feature for the room**.
|
||||||
|
MeetCaptionsConfig:
|
||||||
|
type: object
|
||||||
|
properties:
|
||||||
|
enabled:
|
||||||
|
type: boolean
|
||||||
|
default: true
|
||||||
|
example: true
|
||||||
|
description: >
|
||||||
|
If true, the room will have live captions enabled.<br/>
|
||||||
|
This allows participants to see real-time captions of the all participants' speech during the meeting.<br/>
|
||||||
|
MeetRecordingEncodingPreset:
|
||||||
|
type: string
|
||||||
|
enum:
|
||||||
|
- H264_720P_30
|
||||||
|
- H264_720P_60
|
||||||
|
- H264_1080P_30
|
||||||
|
- H264_1080P_60
|
||||||
|
- PORTRAIT_H264_720P_30
|
||||||
|
- PORTRAIT_H264_720P_60
|
||||||
|
- PORTRAIT_H264_1080P_30
|
||||||
|
- PORTRAIT_H264_1080P_60
|
||||||
|
description: |
|
||||||
|
Predefined encoding presets for recordings. Each preset defines a combination of resolution, frame rate, and codec:
|
||||||
|
- `H264_720P_30`: 1280x720, 30fps, 3000kbps, H.264_MAIN / OPUS **(default)**
|
||||||
|
- `H264_720P_60`: 1280x720, 60fps, 4500kbps, H.264_MAIN / OPUS
|
||||||
|
- `H264_1080P_30`: 1920x1080, 30fps, 4500kbps, H.264_MAIN / OPUS
|
||||||
|
- `H264_1080P_60`: 1920x1080, 60fps, 6000kbps, H.264_MAIN / OPUS
|
||||||
|
- `PORTRAIT_H264_720P_30`: 720x1280, 30fps, 3000kbps, H.264_MAIN / OPUS
|
||||||
|
- `PORTRAIT_H264_720P_60`: 720x1280, 60fps, 4500kbps, H.264_MAIN / OPUS
|
||||||
|
- `PORTRAIT_H264_1080P_30`: 1080x1920, 30fps, 4500kbps, H.264_MAIN / OPUS
|
||||||
|
- `PORTRAIT_H264_1080P_60`: 1080x1920, 60fps, 6000kbps, H.264_MAIN / OPUS
|
||||||
|
example: H264_720P_30
|
||||||
|
MeetRecordingVideoCodec:
|
||||||
|
type: string
|
||||||
|
enum:
|
||||||
|
- DEFAULT_VC
|
||||||
|
- H264_BASELINE
|
||||||
|
- H264_MAIN
|
||||||
|
- H264_HIGH
|
||||||
|
- VP8
|
||||||
|
description: |
|
||||||
|
Video codec options for recording encoding:
|
||||||
|
- `DEFAULT_VC`: Use the default video codec (H.264_MAIN)
|
||||||
|
- `H264_BASELINE`: H.264 Baseline profile
|
||||||
|
- `H264_MAIN`: H.264 Main profile
|
||||||
|
- `H264_HIGH`: H.264 High profile
|
||||||
|
- `VP8`: VP8 codec
|
||||||
|
example: H264_MAIN
|
||||||
|
MeetRecordingAudioCodec:
|
||||||
|
type: string
|
||||||
|
enum:
|
||||||
|
- DEFAULT_AC
|
||||||
|
- OPUS
|
||||||
|
- AAC
|
||||||
|
- AC_MP3
|
||||||
|
description: |
|
||||||
|
Audio codec options for recording encoding:
|
||||||
|
- `DEFAULT_AC`: Use the default audio codec (OPUS)
|
||||||
|
- `OPUS`: Opus codec
|
||||||
|
- `AAC`: AAC codec
|
||||||
|
- `AC_MP3`: MP3 codec
|
||||||
|
example: OPUS
|
||||||
|
MeetRecordingVideoEncodingOptions:
|
||||||
|
type: object
|
||||||
|
required:
|
||||||
|
- width
|
||||||
|
- height
|
||||||
|
- framerate
|
||||||
|
- codec
|
||||||
|
- bitrate
|
||||||
|
- keyFrameInterval
|
||||||
|
- depth
|
||||||
|
properties:
|
||||||
|
width:
|
||||||
|
type: integer
|
||||||
|
minimum: 1
|
||||||
|
example: 1280
|
||||||
|
description: |
|
||||||
|
Video width in pixels
|
||||||
|
height:
|
||||||
|
type: integer
|
||||||
|
minimum: 1
|
||||||
|
example: 720
|
||||||
|
description: |
|
||||||
|
Video height in pixels
|
||||||
|
framerate:
|
||||||
|
type: integer
|
||||||
|
minimum: 1
|
||||||
|
example: 30
|
||||||
|
description: |
|
||||||
|
Frame rate in fps
|
||||||
|
codec:
|
||||||
|
$ref: '#/MeetRecordingVideoCodec'
|
||||||
|
description: |
|
||||||
|
Video codec
|
||||||
|
bitrate:
|
||||||
|
type: integer
|
||||||
|
minimum: 1
|
||||||
|
example: 4500
|
||||||
|
description: |
|
||||||
|
Video bitrate in kbps
|
||||||
|
keyframeInterval:
|
||||||
|
type: number
|
||||||
|
minimum: 0
|
||||||
|
example: 4
|
||||||
|
description: |
|
||||||
|
Keyframe interval in seconds
|
||||||
|
depth:
|
||||||
|
type: integer
|
||||||
|
minimum: 1
|
||||||
|
example: 24
|
||||||
|
description: |
|
||||||
|
Video depth (pixel format) in bits
|
||||||
|
description: |
|
||||||
|
Advanced video encoding options for recordings.
|
||||||
|
MeetRecordingAudioEncodingOptions:
|
||||||
|
type: object
|
||||||
|
required:
|
||||||
|
- codec
|
||||||
|
- bitrate
|
||||||
|
- frequency
|
||||||
|
properties:
|
||||||
|
codec:
|
||||||
|
$ref: '#/MeetRecordingAudioCodec'
|
||||||
|
description: |
|
||||||
|
Audio codec (required when audio is provided)
|
||||||
|
bitrate:
|
||||||
|
type: integer
|
||||||
|
minimum: 1
|
||||||
|
example: 128
|
||||||
|
description: |
|
||||||
|
Audio bitrate in kbps (required when audio is provided)
|
||||||
|
frequency:
|
||||||
|
type: integer
|
||||||
|
minimum: 1
|
||||||
|
example: 44100
|
||||||
|
description: |
|
||||||
|
Audio sample rate in Hz (required when audio is provided)
|
||||||
|
description: |
|
||||||
|
Advanced audio encoding options for recordings.
|
||||||
|
When audio encoding is provided, all fields are required.
|
||||||
|
MeetRecordingEncodingOptions:
|
||||||
|
type: object
|
||||||
|
required:
|
||||||
|
- video
|
||||||
|
- audio
|
||||||
|
properties:
|
||||||
|
video:
|
||||||
|
$ref: '#/MeetRecordingVideoEncodingOptions'
|
||||||
|
description: Video encoding configuration
|
||||||
|
audio:
|
||||||
|
$ref: '#/MeetRecordingAudioEncodingOptions'
|
||||||
|
description: Audio encoding configuration
|
||||||
|
description: |
|
||||||
|
Advanced encoding options for recordings.
|
||||||
|
Use this for fine-grained control over video and audio encoding parameters.
|
||||||
|
Both video and audio configurations are required when using advanced options.
|
||||||
|
For common scenarios, consider using encoding presets instead.
|
||||||
|
example:
|
||||||
|
video:
|
||||||
|
width: 1280
|
||||||
|
height: 720
|
||||||
|
framerate: 30
|
||||||
|
codec: H264_MAIN
|
||||||
|
bitrate: 3000
|
||||||
|
keyFrameInterval: 4
|
||||||
|
audio:
|
||||||
|
codec: OPUS
|
||||||
|
bitrate: 128
|
||||||
|
frequency: 44100
|
||||||
|
|||||||
@ -71,14 +71,14 @@ properties:
|
|||||||
type: string
|
type: string
|
||||||
example: 'http://localhost:6080/room/room-123?secret=123456'
|
example: 'http://localhost:6080/room/room-123?secret=123456'
|
||||||
description: >
|
description: >
|
||||||
The URL for the moderator participants to join the room. The moderator role has special permissions to manage the
|
The URL for moderator room members to access the room. The moderator role has special permissions to manage the
|
||||||
room and participants.
|
room resources and meeting participants.
|
||||||
speakerUrl:
|
speakerUrl:
|
||||||
type: string
|
type: string
|
||||||
example: 'http://localhost:6080/room/room-123?secret=654321'
|
example: 'http://localhost:6080/room/room-123?secret=654321'
|
||||||
description: >
|
description: >
|
||||||
The URL for the speaker participants to join the room. The speaker role has permissions to publish audio and
|
The URL for speaker room members to access the room. The speaker role has permissions to publish audio and
|
||||||
video streams to the room.
|
video streams to the meeting.
|
||||||
status:
|
status:
|
||||||
type: string
|
type: string
|
||||||
enum:
|
enum:
|
||||||
|
|||||||
@ -22,6 +22,11 @@ properties:
|
|||||||
status:
|
status:
|
||||||
type: string
|
type: string
|
||||||
description: The status of the recording.
|
description: The status of the recording.
|
||||||
|
example: active
|
||||||
|
layout:
|
||||||
|
type: string
|
||||||
|
description: The layout of the recording.
|
||||||
|
example: grid
|
||||||
filename:
|
filename:
|
||||||
type: string
|
type: string
|
||||||
description: The name of the recording file.
|
description: The name of the recording file.
|
||||||
|
|||||||
@ -4,51 +4,21 @@ apiKeyHeader:
|
|||||||
in: header
|
in: header
|
||||||
description: >
|
description: >
|
||||||
The API key to authenticate the request. This key is required to access the OpenVidu Meet API.
|
The API key to authenticate the request. This key is required to access the OpenVidu Meet API.
|
||||||
# accessTokenCookie:
|
|
||||||
# type: apiKey
|
|
||||||
# name: OvMeetAccessToken
|
|
||||||
# in: cookie
|
|
||||||
# description: >
|
|
||||||
# The JWT token to authenticate the request in case of consuming the API from the OpenVidu Meet frontend.
|
|
||||||
accessTokenHeader:
|
accessTokenHeader:
|
||||||
type: apiKey
|
type: apiKey
|
||||||
name: Authorization
|
name: Authorization
|
||||||
in: header
|
in: header
|
||||||
description: >
|
description: >
|
||||||
The JWT token to authenticate the request in case of consuming the API from the OpenVidu Meet frontend.
|
The JWT token to authenticate the request in case of consuming the API from the OpenVidu Meet frontend.
|
||||||
# refreshTokenCookie:
|
|
||||||
# type: apiKey
|
|
||||||
# name: OvMeetRefreshToken
|
|
||||||
# in: cookie
|
|
||||||
# description: >
|
|
||||||
# The JWT token to refresh the access token when it expires.
|
|
||||||
refreshTokenHeader:
|
refreshTokenHeader:
|
||||||
type: apiKey
|
type: apiKey
|
||||||
name: X-Refresh-Token
|
name: X-Refresh-Token
|
||||||
in: header
|
in: header
|
||||||
description: >
|
description: >
|
||||||
The JWT token to refresh the access token when it expires.
|
The JWT token to refresh the access token when it expires.
|
||||||
# participantTokenCookie:
|
roomMemberTokenHeader:
|
||||||
# type: apiKey
|
|
||||||
# name: OvMeetParticipantToken
|
|
||||||
# in: cookie
|
|
||||||
# description: >
|
|
||||||
# The JWT token to authenticate the participant when entering the room.
|
|
||||||
participantTokenHeader:
|
|
||||||
type: apiKey
|
type: apiKey
|
||||||
name: X-Participant-Token
|
name: X-Room-Member-Token
|
||||||
in: header
|
in: header
|
||||||
description: >
|
description: >
|
||||||
The JWT token to authenticate the participant when entering the room.
|
The JWT token to authenticate a room member when accessing room and its resources.
|
||||||
# recordingTokenCookie:
|
|
||||||
# type: apiKey
|
|
||||||
# name: OvMeetRecordingToken
|
|
||||||
# in: cookie
|
|
||||||
# description: >
|
|
||||||
# The JWT token containing permissions to access the recordings in a room.
|
|
||||||
recordingTokenHeader:
|
|
||||||
type: apiKey
|
|
||||||
name: X-Recording-Token
|
|
||||||
in: header
|
|
||||||
description: >
|
|
||||||
The JWT token containing permissions to access the recordings in a room.
|
|
||||||
|
|||||||
@ -15,6 +15,8 @@ paths:
|
|||||||
$ref: './paths/rooms.yaml#/~1rooms~1{roomId}'
|
$ref: './paths/rooms.yaml#/~1rooms~1{roomId}'
|
||||||
/rooms/{roomId}/config:
|
/rooms/{roomId}/config:
|
||||||
$ref: './paths/rooms.yaml#/~1rooms~1{roomId}~1config'
|
$ref: './paths/rooms.yaml#/~1rooms~1{roomId}~1config'
|
||||||
|
/rooms/{roomId}/status:
|
||||||
|
$ref: './paths/rooms.yaml#/~1rooms~1{roomId}~1status'
|
||||||
/recordings:
|
/recordings:
|
||||||
$ref: './paths/recordings.yaml#/~1recordings'
|
$ref: './paths/recordings.yaml#/~1recordings'
|
||||||
/recordings/download:
|
/recordings/download:
|
||||||
@ -25,6 +27,8 @@ paths:
|
|||||||
$ref: './paths/recordings.yaml#/~1recordings~1{recordingId}~1media'
|
$ref: './paths/recordings.yaml#/~1recordings~1{recordingId}~1media'
|
||||||
/recordings/{recordingId}/url:
|
/recordings/{recordingId}/url:
|
||||||
$ref: './paths/recordings.yaml#/~1recordings~1{recordingId}~1url'
|
$ref: './paths/recordings.yaml#/~1recordings~1{recordingId}~1url'
|
||||||
|
/recordings/{recordingId}/stop:
|
||||||
|
$ref: './paths/recordings.yaml#/~1recordings~1{recordingId}~1stop'
|
||||||
components:
|
components:
|
||||||
securitySchemes:
|
securitySchemes:
|
||||||
$ref: './components/security.yaml'
|
$ref: './components/security.yaml'
|
||||||
@ -37,7 +41,7 @@ components:
|
|||||||
$ref: './components/schemas/meet-room-config.yaml#/MeetRoomConfig'
|
$ref: './components/schemas/meet-room-config.yaml#/MeetRoomConfig'
|
||||||
MeetRecording:
|
MeetRecording:
|
||||||
$ref: components/schemas/meet-recording.yaml
|
$ref: components/schemas/meet-recording.yaml
|
||||||
Error:
|
|
||||||
$ref: components/schemas/error.yaml
|
|
||||||
MeetWebhookEvent:
|
MeetWebhookEvent:
|
||||||
$ref: components/schemas/meet-webhook.yaml#/MeetWebhookEvent
|
$ref: components/schemas/meet-webhook.yaml#/MeetWebhookEvent
|
||||||
|
Error:
|
||||||
|
$ref: components/schemas/error.yaml
|
||||||
|
|||||||
@ -14,8 +14,8 @@ paths:
|
|||||||
$ref: './paths/internal/auth.yaml#/~1auth~1logout'
|
$ref: './paths/internal/auth.yaml#/~1auth~1logout'
|
||||||
/auth/refresh:
|
/auth/refresh:
|
||||||
$ref: './paths/internal/auth.yaml#/~1auth~1refresh'
|
$ref: './paths/internal/auth.yaml#/~1auth~1refresh'
|
||||||
/auth/api-keys:
|
/api-keys:
|
||||||
$ref: './paths/internal/auth.yaml#/~1auth~1api-keys'
|
$ref: './paths/internal/api-keys.yaml#/~1auth~1api-keys'
|
||||||
/users/profile:
|
/users/profile:
|
||||||
$ref: './paths/internal/users.yaml#/~1users~1profile'
|
$ref: './paths/internal/users.yaml#/~1users~1profile'
|
||||||
/users/change-password:
|
/users/change-password:
|
||||||
@ -28,33 +28,35 @@ paths:
|
|||||||
$ref: './paths/internal/meet-global-config.yaml#/~1config~1security'
|
$ref: './paths/internal/meet-global-config.yaml#/~1config~1security'
|
||||||
/config/rooms/appearance:
|
/config/rooms/appearance:
|
||||||
$ref: './paths/internal/meet-global-config.yaml#/~1config~1rooms~1appearance'
|
$ref: './paths/internal/meet-global-config.yaml#/~1config~1rooms~1appearance'
|
||||||
/rooms/{roomId}/recording-token:
|
/config/captions:
|
||||||
$ref: './paths/internal/rooms.yaml#/~1rooms~1{roomId}~1recording-token'
|
$ref: './paths/internal/meet-global-config.yaml#/~1config~1captions'
|
||||||
|
/rooms/{roomId}/token:
|
||||||
|
$ref: './paths/internal/rooms.yaml#/~1rooms~1{roomId}~1token'
|
||||||
/rooms/{roomId}/roles:
|
/rooms/{roomId}/roles:
|
||||||
$ref: './paths/internal/rooms.yaml#/~1rooms~1{roomId}~1roles'
|
$ref: './paths/internal/rooms.yaml#/~1rooms~1{roomId}~1roles'
|
||||||
/rooms/{roomId}/roles/{secret}:
|
/rooms/{roomId}/roles/{secret}:
|
||||||
$ref: './paths/internal/rooms.yaml#/~1rooms~1{roomId}~1roles~1{secret}'
|
$ref: './paths/internal/rooms.yaml#/~1rooms~1{roomId}~1roles~1{secret}'
|
||||||
/recordings:
|
|
||||||
$ref: './paths/internal/recordings.yaml#/~1recordings'
|
|
||||||
/recordings/{recordingId}/stop:
|
|
||||||
$ref: './paths/internal/recordings.yaml#/~1recordings~1{recordingId}~1stop'
|
|
||||||
/participants/token:
|
|
||||||
$ref: './paths/internal/participants.yaml#/~1participants~1token'
|
|
||||||
/participants/token/refresh:
|
|
||||||
$ref: './paths/internal/participants.yaml#/~1participants~1token~1refresh'
|
|
||||||
/meetings/{roomId}:
|
/meetings/{roomId}:
|
||||||
$ref: './paths/internal/meetings.yaml#/~1meetings~1{roomId}'
|
$ref: './paths/internal/meetings.yaml#/~1meetings~1{roomId}'
|
||||||
/meetings/{roomId}/participants/{participantIdentity}:
|
/meetings/{roomId}/participants/{participantIdentity}:
|
||||||
$ref: './paths/internal/meetings.yaml#/~1meetings~1{roomId}~1participants~1{participantIdentity}'
|
$ref: './paths/internal/meetings.yaml#/~1meetings~1{roomId}~1participants~1{participantIdentity}'
|
||||||
/meetings/{roomId}/participants/{participantIdentity}/role:
|
/meetings/{roomId}/participants/{participantIdentity}/role:
|
||||||
$ref: './paths/internal/meetings.yaml#/~1meetings~1{roomId}~1participants~1{participantIdentity}~1role'
|
$ref: './paths/internal/meetings.yaml#/~1meetings~1{roomId}~1participants~1{participantIdentity}~1role'
|
||||||
|
/ai/assistants:
|
||||||
|
$ref: './paths/internal/ai-assistant.yaml#/~1ai~1assistants'
|
||||||
|
/ai/assistants/{assistantId}:
|
||||||
|
$ref: './paths/internal/ai-assistant.yaml#/~1ai~1assistants~1{assistantId}'
|
||||||
|
/analytics:
|
||||||
|
$ref: './paths/internal/analytics.yaml#/~1analytics'
|
||||||
|
|
||||||
components:
|
components:
|
||||||
securitySchemes:
|
securitySchemes:
|
||||||
$ref: components/security.yaml
|
$ref: components/security.yaml
|
||||||
schemas:
|
schemas:
|
||||||
User:
|
MeetApiKey:
|
||||||
$ref: components/schemas/internal/user.yaml
|
$ref: components/schemas/internal/meet-api-key.yaml
|
||||||
|
MeetUser:
|
||||||
|
$ref: components/schemas/internal/meet-user.yaml
|
||||||
WebhooksConfig:
|
WebhooksConfig:
|
||||||
$ref: components/schemas/internal/webhooks-config.yaml
|
$ref: components/schemas/internal/webhooks-config.yaml
|
||||||
SecurityConfig:
|
SecurityConfig:
|
||||||
@ -65,9 +67,15 @@ components:
|
|||||||
$ref: components/schemas/meet-room-options.yaml
|
$ref: components/schemas/meet-room-options.yaml
|
||||||
MeetRoomConfig:
|
MeetRoomConfig:
|
||||||
$ref: components/schemas/meet-room-config.yaml#/MeetRoomConfig
|
$ref: components/schemas/meet-room-config.yaml#/MeetRoomConfig
|
||||||
MeetRoomRoleAndPermissions:
|
MeetRoomMemberRoleAndPermissions:
|
||||||
$ref: components/schemas/internal/meet-room-role-permissions.yaml
|
$ref: components/schemas/internal/room-member-role-permissions.yaml
|
||||||
|
MeetAnalytics:
|
||||||
|
$ref: components/schemas/internal/meet-analytics.yaml
|
||||||
MeetRecording:
|
MeetRecording:
|
||||||
$ref: components/schemas/meet-recording.yaml
|
$ref: components/schemas/meet-recording.yaml
|
||||||
|
AiAssistantCreateRequest:
|
||||||
|
$ref: components/schemas/internal/ai-assistant-create-request.yaml
|
||||||
|
AiAssistantCreateResponse:
|
||||||
|
$ref: components/schemas/internal/ai-assistant-create-response.yaml
|
||||||
Error:
|
Error:
|
||||||
$ref: components/schemas/error.yaml
|
$ref: components/schemas/error.yaml
|
||||||
|
|||||||
56
meet-ce/backend/openapi/paths/internal/ai-assistant.yaml
Normal file
56
meet-ce/backend/openapi/paths/internal/ai-assistant.yaml
Normal file
@ -0,0 +1,56 @@
|
|||||||
|
/ai/assistants:
|
||||||
|
post:
|
||||||
|
operationId: createAiAssistant
|
||||||
|
summary: Create AI assistant
|
||||||
|
description: |
|
||||||
|
Activates AI assistance.
|
||||||
|
|
||||||
|
> Currently only meeting AI Assistand and `live_captions` capability is supported.
|
||||||
|
tags:
|
||||||
|
- Internal API - AI Assistants
|
||||||
|
security:
|
||||||
|
- roomMemberTokenHeader: []
|
||||||
|
requestBody:
|
||||||
|
$ref: '../../components/requestBodies/internal/create-ai-assistant-request.yaml'
|
||||||
|
responses:
|
||||||
|
'200':
|
||||||
|
$ref: '../../components/responses/internal/success-create-ai-assistant.yaml'
|
||||||
|
'401':
|
||||||
|
$ref: '../../components/responses/unauthorized-error.yaml'
|
||||||
|
'403':
|
||||||
|
$ref: '../../components/responses/forbidden-error.yaml'
|
||||||
|
'422':
|
||||||
|
$ref: '../../components/responses/validation-error.yaml'
|
||||||
|
'500':
|
||||||
|
$ref: '../../components/responses/internal-server-error.yaml'
|
||||||
|
|
||||||
|
/ai/assistants/{assistantId}:
|
||||||
|
delete:
|
||||||
|
operationId: cancelAiAssistant
|
||||||
|
summary: Cancel AI assistant
|
||||||
|
description: |
|
||||||
|
Cancels AI assistant.
|
||||||
|
|
||||||
|
The assistant process (live_captions) is stopped only when the last participant cancels it.
|
||||||
|
tags:
|
||||||
|
- Internal API - AI Assistants
|
||||||
|
security:
|
||||||
|
- roomMemberTokenHeader: []
|
||||||
|
parameters:
|
||||||
|
- in: path
|
||||||
|
name: assistantId
|
||||||
|
required: true
|
||||||
|
schema:
|
||||||
|
type: string
|
||||||
|
minLength: 1
|
||||||
|
description: Identifier of the assistant activation returned by create operation.
|
||||||
|
example: asst_123
|
||||||
|
responses:
|
||||||
|
'204':
|
||||||
|
description: AI assistant canceled successfully.
|
||||||
|
'401':
|
||||||
|
$ref: '../../components/responses/unauthorized-error.yaml'
|
||||||
|
'422':
|
||||||
|
$ref: '../../components/responses/validation-error.yaml'
|
||||||
|
'500':
|
||||||
|
$ref: '../../components/responses/internal-server-error.yaml'
|
||||||
17
meet-ce/backend/openapi/paths/internal/analytics.yaml
Normal file
17
meet-ce/backend/openapi/paths/internal/analytics.yaml
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
/analytics:
|
||||||
|
get:
|
||||||
|
operationId: getAnalytics
|
||||||
|
summary: Get usage analytics
|
||||||
|
description: >
|
||||||
|
Retrieves usage analytics for OpenVidu Meet, including metrics for rooms and recordings.
|
||||||
|
tags:
|
||||||
|
- Internal API - Analytics
|
||||||
|
security:
|
||||||
|
- accessTokenHeader: []
|
||||||
|
responses:
|
||||||
|
'200':
|
||||||
|
$ref: '../../components/responses/internal/success-get-analytics.yaml'
|
||||||
|
'401':
|
||||||
|
$ref: '../../components/responses/unauthorized-error.yaml'
|
||||||
|
'500':
|
||||||
|
$ref: '../../components/responses/internal-server-error.yaml'
|
||||||
49
meet-ce/backend/openapi/paths/internal/api-keys.yaml
Normal file
49
meet-ce/backend/openapi/paths/internal/api-keys.yaml
Normal file
@ -0,0 +1,49 @@
|
|||||||
|
/auth/api-keys:
|
||||||
|
post:
|
||||||
|
operationId: createApiKey
|
||||||
|
summary: Create a new API key
|
||||||
|
description: |
|
||||||
|
Creates a new API key, returning it in the response.
|
||||||
|
|
||||||
|
> **Note:** Only one API key can be created in the system.
|
||||||
|
> If an API key already exists, it will be replaced with the new one.
|
||||||
|
tags:
|
||||||
|
- Internal API - API Keys
|
||||||
|
security:
|
||||||
|
- accessTokenHeader: []
|
||||||
|
responses:
|
||||||
|
'201':
|
||||||
|
$ref: '../../components/responses/internal/success-create-api-key.yaml'
|
||||||
|
'500':
|
||||||
|
$ref: '../../components/responses/internal-server-error.yaml'
|
||||||
|
get:
|
||||||
|
operationId: getApiKeys
|
||||||
|
summary: Get API keys
|
||||||
|
description: |
|
||||||
|
Retrieves the existing API keys.
|
||||||
|
|
||||||
|
> **Note:** Only one API key can exist in the system.
|
||||||
|
> If no API key exists, an empty array will be returned.
|
||||||
|
tags:
|
||||||
|
- Internal API - API Keys
|
||||||
|
security:
|
||||||
|
- accessTokenHeader: []
|
||||||
|
responses:
|
||||||
|
'200':
|
||||||
|
$ref: '../../components/responses/internal/success-get-api-keys.yaml'
|
||||||
|
'500':
|
||||||
|
$ref: '../../components/responses/internal-server-error.yaml'
|
||||||
|
delete:
|
||||||
|
operationId: deleteApiKeys
|
||||||
|
summary: Delete API keys
|
||||||
|
description: >
|
||||||
|
Deletes the existing API keys.
|
||||||
|
tags:
|
||||||
|
- Internal API - API Keys
|
||||||
|
security:
|
||||||
|
- accessTokenHeader: []
|
||||||
|
responses:
|
||||||
|
'200':
|
||||||
|
$ref: '../../components/responses/internal/success-delete-api-key.yaml'
|
||||||
|
'500':
|
||||||
|
$ref: '../../components/responses/internal-server-error.yaml'
|
||||||
@ -46,53 +46,3 @@
|
|||||||
$ref: '../../components/responses/internal/error-invalid-refresh-token.yaml'
|
$ref: '../../components/responses/internal/error-invalid-refresh-token.yaml'
|
||||||
'500':
|
'500':
|
||||||
$ref: '../../components/responses/internal-server-error.yaml'
|
$ref: '../../components/responses/internal-server-error.yaml'
|
||||||
|
|
||||||
/auth/api-keys:
|
|
||||||
post:
|
|
||||||
operationId: createApiKey
|
|
||||||
summary: Create a new API key
|
|
||||||
description: |
|
|
||||||
Creates a new API key, returning it in the response.
|
|
||||||
|
|
||||||
> **Note:** Only one API key can be created in the system.
|
|
||||||
> If an API key already exists, it will be replaced with the new one.
|
|
||||||
tags:
|
|
||||||
- Internal API - Authentication
|
|
||||||
security:
|
|
||||||
- accessTokenHeader: []
|
|
||||||
responses:
|
|
||||||
'201':
|
|
||||||
$ref: '../../components/responses/internal/success-create-api-key.yaml'
|
|
||||||
'500':
|
|
||||||
$ref: '../../components/responses/internal-server-error.yaml'
|
|
||||||
get:
|
|
||||||
operationId: getApiKeys
|
|
||||||
summary: Get API keys
|
|
||||||
description: |
|
|
||||||
Retrieves the existing API keys.
|
|
||||||
|
|
||||||
> **Note:** Only one API key can exist in the system.
|
|
||||||
> If no API key exists, an empty array will be returned.
|
|
||||||
tags:
|
|
||||||
- Internal API - Authentication
|
|
||||||
security:
|
|
||||||
- accessTokenHeader: []
|
|
||||||
responses:
|
|
||||||
'200':
|
|
||||||
$ref: '../../components/responses/internal/success-get-api-keys.yaml'
|
|
||||||
'500':
|
|
||||||
$ref: '../../components/responses/internal-server-error.yaml'
|
|
||||||
delete:
|
|
||||||
operationId: deleteApiKeys
|
|
||||||
summary: Delete API keys
|
|
||||||
description: >
|
|
||||||
Deletes the existing API keys.
|
|
||||||
tags:
|
|
||||||
- Internal API - Authentication
|
|
||||||
security:
|
|
||||||
- accessTokenHeader: []
|
|
||||||
responses:
|
|
||||||
'200':
|
|
||||||
$ref: '../../components/responses/internal/success-delete-api-key.yaml'
|
|
||||||
'500':
|
|
||||||
$ref: '../../components/responses/internal-server-error.yaml'
|
|
||||||
|
|||||||
@ -139,3 +139,20 @@
|
|||||||
$ref: '../../components/responses/validation-error.yaml'
|
$ref: '../../components/responses/validation-error.yaml'
|
||||||
'500':
|
'500':
|
||||||
$ref: '../../components/responses/internal-server-error.yaml'
|
$ref: '../../components/responses/internal-server-error.yaml'
|
||||||
|
|
||||||
|
/config/captions:
|
||||||
|
get:
|
||||||
|
operationId: getCaptionsConfig
|
||||||
|
summary: Get captions config
|
||||||
|
description: >
|
||||||
|
Retrieves the captions configuration from the environment variable MEET_CAPTIONS_ENABLED.
|
||||||
|
This endpoint returns whether captions are enabled in the system.
|
||||||
|
tags:
|
||||||
|
- Internal API - Global Config
|
||||||
|
responses:
|
||||||
|
'200':
|
||||||
|
$ref: '../../components/responses/internal/success-get-captions-config.yaml'
|
||||||
|
'401':
|
||||||
|
$ref: '../../components/responses/unauthorized-error.yaml'
|
||||||
|
'500':
|
||||||
|
$ref: '../../components/responses/internal-server-error.yaml'
|
||||||
|
|||||||
@ -9,15 +9,12 @@
|
|||||||
tags:
|
tags:
|
||||||
- Internal API - Meetings
|
- Internal API - Meetings
|
||||||
security:
|
security:
|
||||||
- participantTokenHeader: []
|
- roomMemberTokenHeader: []
|
||||||
parameters:
|
parameters:
|
||||||
- $ref: '../../components/parameters/room-id-path.yaml'
|
- $ref: '../../components/parameters/room-id-path.yaml'
|
||||||
- $ref: '../../components/parameters/internal/x-participant-role.yaml'
|
|
||||||
responses:
|
responses:
|
||||||
'200':
|
'200':
|
||||||
$ref: '../../components/responses/internal/success-end-meeting.yaml'
|
$ref: '../../components/responses/internal/success-end-meeting.yaml'
|
||||||
'400':
|
|
||||||
$ref: '../../components/responses/internal/error-invalid-participant-role.yaml'
|
|
||||||
'401':
|
'401':
|
||||||
$ref: '../../components/responses/unauthorized-error.yaml'
|
$ref: '../../components/responses/unauthorized-error.yaml'
|
||||||
'403':
|
'403':
|
||||||
@ -35,16 +32,12 @@
|
|||||||
tags:
|
tags:
|
||||||
- Internal API - Meetings
|
- Internal API - Meetings
|
||||||
security:
|
security:
|
||||||
- participantTokenHeader: []
|
- roomMemberTokenHeader: []
|
||||||
parameters:
|
parameters:
|
||||||
- $ref: '../../components/parameters/room-id-path.yaml'
|
- $ref: '../../components/parameters/room-id-path.yaml'
|
||||||
- $ref: '../../components/parameters/internal/participant-identity.yaml'
|
|
||||||
- $ref: '../../components/parameters/internal/x-participant-role.yaml'
|
|
||||||
responses:
|
responses:
|
||||||
'200':
|
'200':
|
||||||
$ref: '../../components/responses/internal/success-kick-participant.yaml'
|
$ref: '../../components/responses/internal/success-kick-participant.yaml'
|
||||||
'400':
|
|
||||||
$ref: '../../components/responses/internal/error-invalid-participant-role.yaml'
|
|
||||||
'401':
|
'401':
|
||||||
$ref: '../../components/responses/unauthorized-error.yaml'
|
$ref: '../../components/responses/unauthorized-error.yaml'
|
||||||
'403':
|
'403':
|
||||||
@ -62,18 +55,14 @@
|
|||||||
tags:
|
tags:
|
||||||
- Internal API - Meetings
|
- Internal API - Meetings
|
||||||
security:
|
security:
|
||||||
- participantTokenHeader: []
|
- roomMemberTokenHeader: []
|
||||||
parameters:
|
parameters:
|
||||||
- $ref: '../../components/parameters/room-id-path.yaml'
|
- $ref: '../../components/parameters/room-id-path.yaml'
|
||||||
- $ref: '../../components/parameters/internal/participant-identity.yaml'
|
|
||||||
- $ref: '../../components/parameters/internal/x-participant-role.yaml'
|
|
||||||
requestBody:
|
requestBody:
|
||||||
$ref: '../../components/requestBodies/internal/update-participant-role-request.yaml'
|
$ref: '../../components/requestBodies/internal/update-participant-role-request.yaml'
|
||||||
responses:
|
responses:
|
||||||
'200':
|
'200':
|
||||||
$ref: '../../components/responses/internal/success-update-participant-role.yaml'
|
$ref: '../../components/responses/internal/success-update-participant-role.yaml'
|
||||||
'400':
|
|
||||||
$ref: '../../components/responses/internal/error-invalid-participant-role.yaml'
|
|
||||||
'401':
|
'401':
|
||||||
$ref: '../../components/responses/unauthorized-error.yaml'
|
$ref: '../../components/responses/unauthorized-error.yaml'
|
||||||
'403':
|
'403':
|
||||||
|
|||||||
@ -1,58 +0,0 @@
|
|||||||
/participants/token:
|
|
||||||
post:
|
|
||||||
operationId: generateParticipantToken
|
|
||||||
summary: Generate token for participant
|
|
||||||
description: >
|
|
||||||
Generates a token for a participant to join an OpenVidu Meet room.
|
|
||||||
tags:
|
|
||||||
- Internal API - Participant
|
|
||||||
security:
|
|
||||||
- accessTokenHeader: []
|
|
||||||
requestBody:
|
|
||||||
$ref: '../../components/requestBodies/internal/participant-token-request.yaml'
|
|
||||||
responses:
|
|
||||||
'200':
|
|
||||||
$ref: '../../components/responses/internal/success-generate-participant-token.yaml'
|
|
||||||
'400':
|
|
||||||
$ref: '../../components/responses/internal/error-invalid-room-secret.yaml'
|
|
||||||
'401':
|
|
||||||
$ref: '../../components/responses/unauthorized-error.yaml'
|
|
||||||
'403':
|
|
||||||
$ref: '../../components/responses/forbidden-error.yaml'
|
|
||||||
'404':
|
|
||||||
$ref: '../../components/responses/error-room-not-found.yaml'
|
|
||||||
'409':
|
|
||||||
$ref: '../../components/responses/internal/error-room-closed.yaml'
|
|
||||||
'422':
|
|
||||||
$ref: '../../components/responses/validation-error.yaml'
|
|
||||||
'500':
|
|
||||||
$ref: '../../components/responses/internal-server-error.yaml'
|
|
||||||
/participants/token/refresh:
|
|
||||||
post:
|
|
||||||
operationId: refreshParticipantToken
|
|
||||||
summary: Refresh token for participant
|
|
||||||
description: >
|
|
||||||
Refresh a token for a participant in an OpenVidu Meet room.
|
|
||||||
tags:
|
|
||||||
- Internal API - Participant
|
|
||||||
security:
|
|
||||||
- accessTokenHeader: []
|
|
||||||
requestBody:
|
|
||||||
$ref: '../../components/requestBodies/internal/participant-token-request.yaml'
|
|
||||||
responses:
|
|
||||||
'200':
|
|
||||||
$ref: '../../components/responses/internal/success-generate-participant-token.yaml'
|
|
||||||
'400':
|
|
||||||
$ref: '../../components/responses/internal/error-invalid-room-secret.yaml'
|
|
||||||
'401':
|
|
||||||
$ref: '../../components/responses/unauthorized-error.yaml'
|
|
||||||
'403':
|
|
||||||
$ref: '../../components/responses/forbidden-error.yaml'
|
|
||||||
'404':
|
|
||||||
$ref: '../../components/responses/internal/error-room-participant-not-found.yaml'
|
|
||||||
'409':
|
|
||||||
$ref: '../../components/responses/internal/error-room-closed.yaml'
|
|
||||||
'422':
|
|
||||||
$ref: '../../components/responses/validation-error.yaml'
|
|
||||||
'500':
|
|
||||||
$ref: '../../components/responses/internal-server-error.yaml'
|
|
||||||
@ -1,65 +0,0 @@
|
|||||||
/recordings:
|
|
||||||
post:
|
|
||||||
operationId: startRecording
|
|
||||||
summary: Start a recording
|
|
||||||
description: >
|
|
||||||
Start a new recording for an OpenVidu Meet room with the specified room ID.
|
|
||||||
tags:
|
|
||||||
- Internal API - Recordings
|
|
||||||
security:
|
|
||||||
- participantTokenHeader: []
|
|
||||||
parameters:
|
|
||||||
- $ref: '../../components/parameters/internal/x-participant-role.yaml'
|
|
||||||
requestBody:
|
|
||||||
$ref: '../../components/requestBodies/internal/start-recording-request.yaml'
|
|
||||||
responses:
|
|
||||||
'201':
|
|
||||||
$ref: '../../components/responses/internal/success-start-recording.yaml'
|
|
||||||
'400':
|
|
||||||
$ref: '../../components/responses/internal/error-invalid-participant-role.yaml'
|
|
||||||
'401':
|
|
||||||
$ref: '../../components/responses/unauthorized-error.yaml'
|
|
||||||
'403':
|
|
||||||
$ref: '../../components/responses/forbidden-error.yaml'
|
|
||||||
'404':
|
|
||||||
$ref: '../../components/responses/error-room-not-found.yaml'
|
|
||||||
'409':
|
|
||||||
$ref: '../../components/responses/internal/error-recording-conflict.yaml'
|
|
||||||
'422':
|
|
||||||
$ref: '../../components/responses/validation-error.yaml'
|
|
||||||
'500':
|
|
||||||
$ref: '../../components/responses/internal-server-error.yaml'
|
|
||||||
'503':
|
|
||||||
$ref: '../../components/responses/internal/error-service-unavailable.yaml'
|
|
||||||
/recordings/{recordingId}/stop:
|
|
||||||
post:
|
|
||||||
operationId: stopRecording
|
|
||||||
summary: Stop a recording
|
|
||||||
description: |
|
|
||||||
Stops a recording with the specified recording ID.
|
|
||||||
|
|
||||||
> **Note:** The recording must be in an `active` state; otherwise, a 409 error is returned.
|
|
||||||
tags:
|
|
||||||
- Internal API - Recordings
|
|
||||||
security:
|
|
||||||
- participantTokenHeader: []
|
|
||||||
parameters:
|
|
||||||
- $ref: '../../components/parameters/recording-id.yaml'
|
|
||||||
- $ref: '../../components/parameters/internal/x-participant-role.yaml'
|
|
||||||
responses:
|
|
||||||
'202':
|
|
||||||
$ref: '../../components/responses/internal/success-stop-recording.yaml'
|
|
||||||
'400':
|
|
||||||
$ref: '../../components/responses/internal/error-invalid-participant-role.yaml'
|
|
||||||
'401':
|
|
||||||
$ref: '../../components/responses/unauthorized-error.yaml'
|
|
||||||
'403':
|
|
||||||
$ref: '../../components/responses/forbidden-error.yaml'
|
|
||||||
'404':
|
|
||||||
$ref: '../../components/responses/error-recording-not-found.yaml'
|
|
||||||
'409':
|
|
||||||
$ref: '../../components/responses/internal/error-recording-not-active.yaml'
|
|
||||||
'422':
|
|
||||||
$ref: '../../components/responses/validation-error.yaml'
|
|
||||||
'500':
|
|
||||||
$ref: '../../components/responses/internal-server-error.yaml'
|
|
||||||
@ -1,10 +1,9 @@
|
|||||||
/rooms/{roomId}/recording-token:
|
/rooms/{roomId}/token:
|
||||||
post:
|
post:
|
||||||
operationId: generateRecordingToken
|
operationId: generateRoomMemberToken
|
||||||
summary: Generate recording token
|
summary: Generate room member token
|
||||||
description: >
|
description: >
|
||||||
Generates a token with recording permissions for a specified OpenVidu Meet room.
|
Generates a token for a user to access an OpenVidu Meet room and its resources.
|
||||||
This token can be used to access the recordings in a room.
|
|
||||||
tags:
|
tags:
|
||||||
- Internal API - Rooms
|
- Internal API - Rooms
|
||||||
security:
|
security:
|
||||||
@ -12,10 +11,10 @@
|
|||||||
parameters:
|
parameters:
|
||||||
- $ref: '../../components/parameters/room-id-path.yaml'
|
- $ref: '../../components/parameters/room-id-path.yaml'
|
||||||
requestBody:
|
requestBody:
|
||||||
$ref: '../../components/requestBodies/internal/recording-token-request.yaml'
|
$ref: '../../components/requestBodies/internal/room-member-token-request.yaml'
|
||||||
responses:
|
responses:
|
||||||
'200':
|
'200':
|
||||||
$ref: '../../components/responses/internal/success-generate-recording-token.yaml'
|
$ref: '../../components/responses/internal/success-generate-room-member-token.yaml'
|
||||||
'400':
|
'400':
|
||||||
$ref: '../../components/responses/internal/error-invalid-room-secret.yaml'
|
$ref: '../../components/responses/internal/error-invalid-room-secret.yaml'
|
||||||
'401':
|
'401':
|
||||||
@ -23,24 +22,26 @@
|
|||||||
'403':
|
'403':
|
||||||
$ref: '../../components/responses/forbidden-error.yaml'
|
$ref: '../../components/responses/forbidden-error.yaml'
|
||||||
'404':
|
'404':
|
||||||
$ref: '../../components/responses/internal/error-room-metadata-not-found.yaml'
|
$ref: '../../components/responses/internal/error-room-participant-not-found.yaml'
|
||||||
|
'409':
|
||||||
|
$ref: '../../components/responses/internal/error-room-closed.yaml'
|
||||||
'422':
|
'422':
|
||||||
$ref: '../../components/responses/validation-error.yaml'
|
$ref: '../../components/responses/validation-error.yaml'
|
||||||
'500':
|
'500':
|
||||||
$ref: '../../components/responses/internal-server-error.yaml'
|
$ref: '../../components/responses/internal-server-error.yaml'
|
||||||
/rooms/{roomId}/roles:
|
/rooms/{roomId}/roles:
|
||||||
get:
|
get:
|
||||||
operationId: getRoomRolesAndPermissions
|
operationId: getRoomMemberRolesAndPermissions
|
||||||
summary: Get room roles and permissions
|
summary: Get room member roles and permissions
|
||||||
description: >
|
description: >
|
||||||
Retrieves the roles and associated permissions that a participant can have in a specified OpenVidu Meet room.
|
Retrieves the roles and associated permissions that a user can have as a member of a specified OpenVidu Meet room.
|
||||||
tags:
|
tags:
|
||||||
- Internal API - Rooms
|
- Internal API - Rooms
|
||||||
parameters:
|
parameters:
|
||||||
- $ref: '../../components/parameters/room-id-path.yaml'
|
- $ref: '../../components/parameters/room-id-path.yaml'
|
||||||
responses:
|
responses:
|
||||||
'200':
|
'200':
|
||||||
$ref: '../../components/responses/internal/success-get-room-roles.yaml'
|
$ref: '../../components/responses/internal/success-get-room-member-roles.yaml'
|
||||||
'404':
|
'404':
|
||||||
$ref: '../../components/responses/error-room-not-found.yaml'
|
$ref: '../../components/responses/error-room-not-found.yaml'
|
||||||
'422':
|
'422':
|
||||||
@ -52,10 +53,10 @@
|
|||||||
operationId: getRoomRoleAndPermissions
|
operationId: getRoomRoleAndPermissions
|
||||||
summary: Get room role and permissions
|
summary: Get room role and permissions
|
||||||
description: |
|
description: |
|
||||||
Retrieves the role and associated permissions that a participant will have in a specified OpenVidu Meet room
|
Retrieves the role and associated permissions that a user will have as a member of a specified OpenVidu Meet room
|
||||||
when using the URL thant contains the given secret value.
|
when using the URL that contains the given secret value.
|
||||||
|
|
||||||
This endpoint is useful for checking the participant's role and permissions before joining the room.
|
This endpoint is useful for checking the user's role and permissions before accessing the room.
|
||||||
tags:
|
tags:
|
||||||
- Internal API - Rooms
|
- Internal API - Rooms
|
||||||
parameters:
|
parameters:
|
||||||
@ -63,7 +64,7 @@
|
|||||||
- $ref: '../../components/parameters/internal/secret.yaml'
|
- $ref: '../../components/parameters/internal/secret.yaml'
|
||||||
responses:
|
responses:
|
||||||
'200':
|
'200':
|
||||||
$ref: '../../components/responses/internal/success-get-room-role.yaml'
|
$ref: '../../components/responses/internal/success-get-room-member-role.yaml'
|
||||||
'400':
|
'400':
|
||||||
$ref: '../../components/responses/internal/error-invalid-room-secret.yaml'
|
$ref: '../../components/responses/internal/error-invalid-room-secret.yaml'
|
||||||
'404':
|
'404':
|
||||||
|
|||||||
@ -1,4 +1,42 @@
|
|||||||
/recordings:
|
/recordings:
|
||||||
|
post:
|
||||||
|
operationId: startRecording
|
||||||
|
summary: Start a recording
|
||||||
|
description: >
|
||||||
|
Start a new recording for an OpenVidu Meet room with the specified room ID.
|
||||||
|
|
||||||
|
|
||||||
|
By default, the recording will use the configuration defined in the room's settings.
|
||||||
|
However, you can optionally provide a configuration override in the request body to customize
|
||||||
|
the recording settings (e.g., layout) for this specific recording session.
|
||||||
|
|
||||||
|
|
||||||
|
If a configuration override is provided, those values will take precedence over the room's configuration.
|
||||||
|
tags:
|
||||||
|
- OpenVidu Meet - Recordings
|
||||||
|
security:
|
||||||
|
- apiKeyHeader: []
|
||||||
|
- roomMemberTokenHeader: []
|
||||||
|
requestBody:
|
||||||
|
$ref: '../components/requestBodies/start-recording-request.yaml'
|
||||||
|
responses:
|
||||||
|
'201':
|
||||||
|
$ref: '../components/responses/success-start-recording.yaml'
|
||||||
|
'401':
|
||||||
|
$ref: '../components/responses/unauthorized-error.yaml'
|
||||||
|
'403':
|
||||||
|
$ref: '../components/responses/forbidden-not-allowed-error.yaml'
|
||||||
|
'404':
|
||||||
|
$ref: '../components/responses/error-room-not-found.yaml'
|
||||||
|
'409':
|
||||||
|
$ref: '../components/responses/error-recording-conflict.yaml'
|
||||||
|
'422':
|
||||||
|
$ref: '../components/responses/validation-error.yaml'
|
||||||
|
'500':
|
||||||
|
$ref: '../components/responses/internal-server-error.yaml'
|
||||||
|
'503':
|
||||||
|
$ref: '../components/responses/error-service-unavailable.yaml'
|
||||||
|
|
||||||
get:
|
get:
|
||||||
operationId: getRecordings
|
operationId: getRecordings
|
||||||
summary: Get all recordings
|
summary: Get all recordings
|
||||||
@ -6,20 +44,25 @@
|
|||||||
Retrieves a paginated list of all recordings available in the system.
|
Retrieves a paginated list of all recordings available in the system.
|
||||||
You can apply filters to narrow down the results based on specific criteria.
|
You can apply filters to narrow down the results based on specific criteria.
|
||||||
|
|
||||||
> **Note:** If this endpoint is called using the `recordingTokenHeader` authentication method,
|
By default, the recordings are sorted by start date in descending order (newest first).
|
||||||
|
|
||||||
|
> **Note:** If this endpoint is called using the `roomMemberTokenHeader` authentication method,
|
||||||
> the `roomId` filter will be ignored and only recordings associated with the room included in the token will be returned.
|
> the `roomId` filter will be ignored and only recordings associated with the room included in the token will be returned.
|
||||||
tags:
|
tags:
|
||||||
- OpenVidu Meet - Recordings
|
- OpenVidu Meet - Recordings
|
||||||
security:
|
security:
|
||||||
- apiKeyHeader: []
|
- apiKeyHeader: []
|
||||||
- accessTokenHeader: []
|
- accessTokenHeader: []
|
||||||
- recordingTokenHeader: []
|
- roomMemberTokenHeader: []
|
||||||
parameters:
|
parameters:
|
||||||
# - $ref: '../components/parameters/recording-status.yaml'
|
|
||||||
- $ref: '../components/parameters/recording-fields.yaml'
|
|
||||||
- $ref: '../components/parameters/room-id-query.yaml'
|
- $ref: '../components/parameters/room-id-query.yaml'
|
||||||
|
- $ref: '../components/parameters/room-name.yaml'
|
||||||
|
- $ref: '../components/parameters/recording-status.yaml'
|
||||||
|
- $ref: '../components/parameters/recording-fields.yaml'
|
||||||
- $ref: '../components/parameters/max-items.yaml'
|
- $ref: '../components/parameters/max-items.yaml'
|
||||||
- $ref: '../components/parameters/next-page-token.yaml'
|
- $ref: '../components/parameters/next-page-token.yaml'
|
||||||
|
- $ref: '../components/parameters/sort-field.yaml'
|
||||||
|
- $ref: '../components/parameters/sort-order.yaml'
|
||||||
responses:
|
responses:
|
||||||
'200':
|
'200':
|
||||||
$ref: '../components/responses/success-get-recordings.yaml'
|
$ref: '../components/responses/success-get-recordings.yaml'
|
||||||
@ -38,7 +81,7 @@
|
|||||||
description: |
|
description: |
|
||||||
Deletes multiple recordings at once with the specified recording IDs.
|
Deletes multiple recordings at once with the specified recording IDs.
|
||||||
|
|
||||||
> **Note:** If this endpoint is called using the `recordingTokenHeader` authentication method,
|
> **Note:** If this endpoint is called using the `roomMemberTokenHeader` authentication method,
|
||||||
> all specified recordings must belong to the same room included in the token.
|
> all specified recordings must belong to the same room included in the token.
|
||||||
> If a recording does not belong to that room, it will not be deleted.
|
> If a recording does not belong to that room, it will not be deleted.
|
||||||
tags:
|
tags:
|
||||||
@ -46,7 +89,7 @@
|
|||||||
security:
|
security:
|
||||||
- apiKeyHeader: []
|
- apiKeyHeader: []
|
||||||
- accessTokenHeader: []
|
- accessTokenHeader: []
|
||||||
- recordingTokenHeader: []
|
- roomMemberTokenHeader: []
|
||||||
parameters:
|
parameters:
|
||||||
- $ref: '../components/parameters/recording-ids.yaml'
|
- $ref: '../components/parameters/recording-ids.yaml'
|
||||||
responses:
|
responses:
|
||||||
@ -71,7 +114,7 @@
|
|||||||
Downloads multiple recordings as a ZIP file with the specified recording IDs.
|
Downloads multiple recordings as a ZIP file with the specified recording IDs.
|
||||||
The ZIP file will contain all recordings in MP4 format.
|
The ZIP file will contain all recordings in MP4 format.
|
||||||
|
|
||||||
> **Note:** If this endpoint is called using the `recordingTokenHeader` authentication method,
|
> **Note:** If this endpoint is called using the `roomMemberTokenHeader` authentication method,
|
||||||
> all specified recordings must belong to the same room included in the token.
|
> all specified recordings must belong to the same room included in the token.
|
||||||
> If a recording does not belong to that room, it will not be included in the ZIP file.
|
> If a recording does not belong to that room, it will not be included in the ZIP file.
|
||||||
tags:
|
tags:
|
||||||
@ -79,7 +122,7 @@
|
|||||||
security:
|
security:
|
||||||
- apiKeyHeader: []
|
- apiKeyHeader: []
|
||||||
- accessTokenHeader: []
|
- accessTokenHeader: []
|
||||||
- recordingTokenHeader: []
|
- roomMemberTokenHeader: []
|
||||||
parameters:
|
parameters:
|
||||||
- $ref: '../components/parameters/recording-ids.yaml'
|
- $ref: '../components/parameters/recording-ids.yaml'
|
||||||
responses:
|
responses:
|
||||||
@ -120,7 +163,7 @@
|
|||||||
security:
|
security:
|
||||||
- apiKeyHeader: []
|
- apiKeyHeader: []
|
||||||
- accessTokenHeader: []
|
- accessTokenHeader: []
|
||||||
- recordingTokenHeader: []
|
- roomMemberTokenHeader: []
|
||||||
parameters:
|
parameters:
|
||||||
- $ref: '../components/parameters/recording-id.yaml'
|
- $ref: '../components/parameters/recording-id.yaml'
|
||||||
- $ref: '../components/parameters/recording-secret.yaml'
|
- $ref: '../components/parameters/recording-secret.yaml'
|
||||||
@ -152,7 +195,7 @@
|
|||||||
security:
|
security:
|
||||||
- apiKeyHeader: []
|
- apiKeyHeader: []
|
||||||
- accessTokenHeader: []
|
- accessTokenHeader: []
|
||||||
- recordingTokenHeader: []
|
- roomMemberTokenHeader: []
|
||||||
parameters:
|
parameters:
|
||||||
- $ref: '../components/parameters/recording-id.yaml'
|
- $ref: '../components/parameters/recording-id.yaml'
|
||||||
responses:
|
responses:
|
||||||
@ -185,7 +228,7 @@
|
|||||||
security:
|
security:
|
||||||
- apiKeyHeader: []
|
- apiKeyHeader: []
|
||||||
- accessTokenHeader: []
|
- accessTokenHeader: []
|
||||||
- recordingTokenHeader: []
|
- roomMemberTokenHeader: []
|
||||||
parameters:
|
parameters:
|
||||||
- $ref: '../components/parameters/recording-id.yaml'
|
- $ref: '../components/parameters/recording-id.yaml'
|
||||||
- $ref: '../components/parameters/recording-secret.yaml'
|
- $ref: '../components/parameters/recording-secret.yaml'
|
||||||
@ -244,6 +287,36 @@
|
|||||||
$ref: '../components/responses/validation-error.yaml'
|
$ref: '../components/responses/validation-error.yaml'
|
||||||
'500':
|
'500':
|
||||||
$ref: '../components/responses/internal-server-error.yaml'
|
$ref: '../components/responses/internal-server-error.yaml'
|
||||||
|
/recordings/{recordingId}/stop:
|
||||||
|
post:
|
||||||
|
operationId: stopRecording
|
||||||
|
summary: Stop a recording
|
||||||
|
description: |
|
||||||
|
Stops a recording with the specified recording ID.
|
||||||
|
|
||||||
|
> **Note:** The recording must be in an `active` state; otherwise, a 409 error is returned.
|
||||||
|
tags:
|
||||||
|
- OpenVidu Meet - Recordings
|
||||||
|
security:
|
||||||
|
- apiKeyHeader: []
|
||||||
|
- roomMemberTokenHeader: []
|
||||||
|
parameters:
|
||||||
|
- $ref: '../components/parameters/recording-id.yaml'
|
||||||
|
responses:
|
||||||
|
'202':
|
||||||
|
$ref: '../components/responses/success-stop-recording.yaml'
|
||||||
|
'401':
|
||||||
|
$ref: '../components/responses/unauthorized-error.yaml'
|
||||||
|
'403':
|
||||||
|
$ref: '../components/responses/forbidden-error.yaml'
|
||||||
|
'404':
|
||||||
|
$ref: '../components/responses/error-recording-not-found.yaml'
|
||||||
|
'409':
|
||||||
|
$ref: '../components/responses/error-recording-not-active.yaml'
|
||||||
|
'422':
|
||||||
|
$ref: '../components/responses/validation-error.yaml'
|
||||||
|
'500':
|
||||||
|
$ref: '../components/responses/internal-server-error.yaml'
|
||||||
/recordings/{recordingId}/url:
|
/recordings/{recordingId}/url:
|
||||||
get:
|
get:
|
||||||
operationId: getRecordingUrl
|
operationId: getRecordingUrl
|
||||||
@ -259,7 +332,7 @@
|
|||||||
security:
|
security:
|
||||||
- apiKeyHeader: []
|
- apiKeyHeader: []
|
||||||
- accessTokenHeader: []
|
- accessTokenHeader: []
|
||||||
- recordingTokenHeader: []
|
- roomMemberTokenHeader: []
|
||||||
parameters:
|
parameters:
|
||||||
- $ref: '../components/parameters/recording-id.yaml'
|
- $ref: '../components/parameters/recording-id.yaml'
|
||||||
# - $ref: '../components/parameters/private-access.yaml'
|
# - $ref: '../components/parameters/private-access.yaml'
|
||||||
|
|||||||
@ -29,6 +29,8 @@
|
|||||||
description: >
|
description: >
|
||||||
Retrieves a paginated list of all rooms available in the system.
|
Retrieves a paginated list of all rooms available in the system.
|
||||||
You can apply filters to narrow down the results based on specific criteria.
|
You can apply filters to narrow down the results based on specific criteria.
|
||||||
|
|
||||||
|
By default, the rooms are sorted by creation date in descending order (newest first).
|
||||||
tags:
|
tags:
|
||||||
- OpenVidu Meet - Rooms
|
- OpenVidu Meet - Rooms
|
||||||
security:
|
security:
|
||||||
@ -36,9 +38,12 @@
|
|||||||
- accessTokenHeader: []
|
- accessTokenHeader: []
|
||||||
parameters:
|
parameters:
|
||||||
- $ref: '../components/parameters/room-name.yaml'
|
- $ref: '../components/parameters/room-name.yaml'
|
||||||
|
- $ref: '../components/parameters/room-status.yaml'
|
||||||
- $ref: '../components/parameters/room-fields.yaml'
|
- $ref: '../components/parameters/room-fields.yaml'
|
||||||
- $ref: '../components/parameters/max-items.yaml'
|
- $ref: '../components/parameters/max-items.yaml'
|
||||||
- $ref: '../components/parameters/next-page-token.yaml'
|
- $ref: '../components/parameters/next-page-token.yaml'
|
||||||
|
- $ref: '../components/parameters/sort-field.yaml'
|
||||||
|
- $ref: '../components/parameters/sort-order.yaml'
|
||||||
responses:
|
responses:
|
||||||
'200':
|
'200':
|
||||||
$ref: '../components/responses/success-get-rooms.yaml'
|
$ref: '../components/responses/success-get-rooms.yaml'
|
||||||
@ -56,10 +61,10 @@
|
|||||||
description: |
|
description: |
|
||||||
Delete multiple OpenVidu Meet rooms at once with the specified room IDs.
|
Delete multiple OpenVidu Meet rooms at once with the specified room IDs.
|
||||||
|
|
||||||
If any of the rooms have active meetings or recordings,
|
If any of the rooms have active meetings or recordings,
|
||||||
deletion behavior is determined by the provided `withMeeting` and `withRecordings` deletion policies.
|
deletion behavior is determined by the provided `withMeeting` and `withRecordings` deletion policies.
|
||||||
|
|
||||||
Depending on these policies, the rooms may be deleted/closed immediately, scheduled to be deleted/closed once the meetings end,
|
Depending on these policies, the rooms may be deleted/closed immediately, scheduled to be deleted/closed once the meetings end,
|
||||||
or the operation may fail if deletion is not permitted.
|
or the operation may fail if deletion is not permitted.
|
||||||
tags:
|
tags:
|
||||||
- OpenVidu Meet - Rooms
|
- OpenVidu Meet - Rooms
|
||||||
@ -94,16 +99,13 @@
|
|||||||
security:
|
security:
|
||||||
- apiKeyHeader: []
|
- apiKeyHeader: []
|
||||||
- accessTokenHeader: []
|
- accessTokenHeader: []
|
||||||
- participantTokenHeader: []
|
- roomMemberTokenHeader: []
|
||||||
parameters:
|
parameters:
|
||||||
- $ref: '../components/parameters/room-id-path.yaml'
|
- $ref: '../components/parameters/room-id-path.yaml'
|
||||||
- $ref: '../components/parameters/room-fields.yaml'
|
- $ref: '../components/parameters/room-fields.yaml'
|
||||||
- $ref: '../components/parameters/internal/x-participant-role.yaml'
|
|
||||||
responses:
|
responses:
|
||||||
'200':
|
'200':
|
||||||
$ref: '../components/responses/success-get-room.yaml'
|
$ref: '../components/responses/success-get-room.yaml'
|
||||||
'400':
|
|
||||||
$ref: '../components/responses/internal/error-invalid-participant-role.yaml'
|
|
||||||
'401':
|
'401':
|
||||||
$ref: '../components/responses/unauthorized-error.yaml'
|
$ref: '../components/responses/unauthorized-error.yaml'
|
||||||
'403':
|
'403':
|
||||||
@ -120,10 +122,10 @@
|
|||||||
description: |
|
description: |
|
||||||
Deletes the specified OpenVidu Meet room by its room ID.
|
Deletes the specified OpenVidu Meet room by its room ID.
|
||||||
|
|
||||||
If the room has an active meeting or existing recordings,
|
If the room has an active meeting or existing recordings,
|
||||||
deletion behavior is determined by the provided `withMeeting` and `withRecordings` deletion policies.
|
deletion behavior is determined by the provided `withMeeting` and `withRecordings` deletion policies.
|
||||||
|
|
||||||
Depending on these policies, the room may be deleted/closed immediately, scheduled to be deleted/closed once the meeting ends,
|
Depending on these policies, the room may be deleted/closed immediately, scheduled to be deleted/closed once the meeting ends,
|
||||||
or the operation may fail if deletion is not permitted.
|
or the operation may fail if deletion is not permitted.
|
||||||
tags:
|
tags:
|
||||||
- OpenVidu Meet - Rooms
|
- OpenVidu Meet - Rooms
|
||||||
@ -162,15 +164,12 @@
|
|||||||
security:
|
security:
|
||||||
- apiKeyHeader: []
|
- apiKeyHeader: []
|
||||||
- accessTokenHeader: []
|
- accessTokenHeader: []
|
||||||
- participantTokenHeader: []
|
- roomMemberTokenHeader: []
|
||||||
parameters:
|
parameters:
|
||||||
- $ref: '../components/parameters/room-id-path.yaml'
|
- $ref: '../components/parameters/room-id-path.yaml'
|
||||||
- $ref: '../components/parameters/internal/x-participant-role.yaml'
|
|
||||||
responses:
|
responses:
|
||||||
'200':
|
'200':
|
||||||
$ref: '../components/responses/success-get-room-config.yaml'
|
$ref: '../components/responses/success-get-room-config.yaml'
|
||||||
'400':
|
|
||||||
$ref: '../components/responses/internal/error-invalid-participant-role.yaml'
|
|
||||||
'401':
|
'401':
|
||||||
$ref: '../components/responses/unauthorized-error.yaml'
|
$ref: '../components/responses/unauthorized-error.yaml'
|
||||||
'403':
|
'403':
|
||||||
@ -204,6 +203,39 @@
|
|||||||
$ref: '../components/responses/forbidden-error.yaml'
|
$ref: '../components/responses/forbidden-error.yaml'
|
||||||
'404':
|
'404':
|
||||||
$ref: '../components/responses/error-room-not-found.yaml'
|
$ref: '../components/responses/error-room-not-found.yaml'
|
||||||
|
'409':
|
||||||
|
$ref: '../components/responses/error-room-active-meeting.yaml'
|
||||||
|
'422':
|
||||||
|
$ref: '../components/responses/validation-error.yaml'
|
||||||
|
'500':
|
||||||
|
$ref: '../components/responses/internal-server-error.yaml'
|
||||||
|
/rooms/{roomId}/status:
|
||||||
|
put:
|
||||||
|
operationId: updateRoomStatus
|
||||||
|
summary: Update room status
|
||||||
|
description: >
|
||||||
|
Updates the status of an OpenVidu Meet room with the specified room ID.
|
||||||
|
This can be used to open or close the room for new participants.
|
||||||
|
tags:
|
||||||
|
- OpenVidu Meet - Rooms
|
||||||
|
security:
|
||||||
|
- apiKeyHeader: []
|
||||||
|
- accessTokenHeader: []
|
||||||
|
parameters:
|
||||||
|
- $ref: '../components/parameters/room-id-path.yaml'
|
||||||
|
requestBody:
|
||||||
|
$ref: '../components/requestBodies/update-room-status-request.yaml'
|
||||||
|
responses:
|
||||||
|
'200':
|
||||||
|
$ref: '../components/responses/success-update-room-status.yaml'
|
||||||
|
'202':
|
||||||
|
$ref: '../components/responses/success-room-schedule-closure.yaml'
|
||||||
|
'401':
|
||||||
|
$ref: '../components/responses/unauthorized-error.yaml'
|
||||||
|
'403':
|
||||||
|
$ref: '../components/responses/forbidden-error.yaml'
|
||||||
|
'404':
|
||||||
|
$ref: '../components/responses/error-room-not-found.yaml'
|
||||||
'422':
|
'422':
|
||||||
$ref: '../components/responses/validation-error.yaml'
|
$ref: '../components/responses/validation-error.yaml'
|
||||||
'500':
|
'500':
|
||||||
|
|||||||
@ -4,15 +4,19 @@
|
|||||||
description: Operations related to managing OpenVidu Meet recordings
|
description: Operations related to managing OpenVidu Meet recordings
|
||||||
- name: Internal API - Authentication
|
- name: Internal API - Authentication
|
||||||
description: Authentication operations
|
description: Authentication operations
|
||||||
|
- name: Internal API - API Keys
|
||||||
|
description: Operations related to managing API keys in OpenVidu Meet
|
||||||
|
- name: Internal API - Analytics
|
||||||
|
description: Operations related to usage analytics in OpenVidu Meet
|
||||||
- name: Internal API - Users
|
- name: Internal API - Users
|
||||||
description: Operations related to managing users in OpenVidu Meet
|
description: Operations related to managing users in OpenVidu Meet
|
||||||
- name: Internal API - Global Config
|
- name: Internal API - Global Config
|
||||||
description: Operations related to managing global config in OpenVidu Meet
|
description: Operations related to managing global config in OpenVidu Meet
|
||||||
- name: Internal API - Rooms
|
- name: Internal API - Rooms
|
||||||
description: Operations related to managing OpenVidu Meet rooms
|
description: Operations related to managing OpenVidu Meet rooms
|
||||||
- name: Internal API - Participant
|
|
||||||
description: Operations related to managing participants in OpenVidu Meet rooms
|
|
||||||
- name: Internal API - Meetings
|
- name: Internal API - Meetings
|
||||||
description: Operations related to managing meetings in OpenVidu Meet rooms
|
description: Operations related to managing meetings in OpenVidu Meet rooms
|
||||||
|
- name: Internal API - AI Assistants
|
||||||
|
description: High-level operations to manage AI assistance capabilities in meetings
|
||||||
- name: Internal API - Recordings
|
- name: Internal API - Recordings
|
||||||
description: Operations related to managing OpenVidu Meet recordings
|
description: Operations related to managing OpenVidu Meet recordings
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@openvidu-meet/backend",
|
"name": "@openvidu-meet/backend",
|
||||||
"version": "3.4.1",
|
"version": "3.6.0",
|
||||||
"description": "OpenVidu Meet Backend",
|
"description": "OpenVidu Meet Backend",
|
||||||
"author": "OpenVidu",
|
"author": "OpenVidu",
|
||||||
"license": "Apache-2.0",
|
"license": "Apache-2.0",
|
||||||
@ -27,7 +27,7 @@
|
|||||||
"package.json"
|
"package.json"
|
||||||
],
|
],
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"build": "tsc -p tsconfig.prod.json",
|
"build": "tsc -p tsconfig.prod.json && pnpm run doc:api",
|
||||||
"build:watch": "tsc -p tsconfig.prod.json --watch",
|
"build:watch": "tsc -p tsconfig.prod.json --watch",
|
||||||
"doc:api": "mkdir -p public/openapi && cd openapi && openapi-generate-html -i openvidu-meet-api.yaml --ui=stoplight --theme=light --title 'OpenVidu Meet REST API' --description 'OpenVidu Meet REST API' -o ../public/openapi/public.html",
|
"doc:api": "mkdir -p public/openapi && cd openapi && openapi-generate-html -i openvidu-meet-api.yaml --ui=stoplight --theme=light --title 'OpenVidu Meet REST API' --description 'OpenVidu Meet REST API' -o ../public/openapi/public.html",
|
||||||
"doc:internal-api": "mkdir -p public/openapi && cd openapi && openapi-generate-html -i openvidu-meet-internal-api.yaml --ui=stoplight --theme=dark --title 'OpenVidu Meet Internal REST API' --description 'OpenVidu Meet Internal REST API' -o ../public/openapi/internal.html",
|
"doc:internal-api": "mkdir -p public/openapi && cd openapi && openapi-generate-html -i openvidu-meet-internal-api.yaml --ui=stoplight --theme=dark --title 'OpenVidu Meet Internal REST API' --description 'OpenVidu Meet Internal REST API' -o ../public/openapi/internal.html",
|
||||||
@ -35,23 +35,29 @@
|
|||||||
"start:dev": "NODE_ENV=development concurrently -k -n server,typecheck -c cyan,yellow \"pnpm tsx watch --clear-screen=false --include src ./src/server.ts\" \"pnpm run dev:typecheck\"",
|
"start:dev": "NODE_ENV=development concurrently -k -n server,typecheck -c cyan,yellow \"pnpm tsx watch --clear-screen=false --include src ./src/server.ts\" \"pnpm run dev:typecheck\"",
|
||||||
"dev:typecheck": "node ../../scripts/dev/backend-type-checker.mjs",
|
"dev:typecheck": "node ../../scripts/dev/backend-type-checker.mjs",
|
||||||
"package:build": "pnpm run build:prod && pnpm pack",
|
"package:build": "pnpm run build:prod && pnpm pack",
|
||||||
"test:integration-rooms": "node --experimental-vm-modules ../../node_modules/jest/bin/jest.js --runInBand --forceExit --testPathPattern 'tests/integration/api/rooms' --ci --reporters=default --reporters=jest-junit",
|
"test:integration-room-management": "node --experimental-vm-modules ../../node_modules/jest/bin/jest.js --runInBand --forceExit --testPathPattern \"tests/integration/api/(rooms|meetings)\" --ci --reporters=default --reporters=jest-junit",
|
||||||
"test:integration-recordings": "node --experimental-vm-modules ../../node_modules/.bin/jest --maxWorkers=1 --maxConcurrency=1 --forceExit --testPathPattern \"tests/integration/api/recordings\" --ci --reporters=default --reporters=jest-junit",
|
"test:integration-rooms": "node --experimental-vm-modules ../../node_modules/jest/bin/jest.js --runInBand --forceExit --testPathPattern 'tests/integration/api/rooms' --ci --reporters=default --reporters=jest-junit",
|
||||||
"test:integration-webhooks": "node --experimental-vm-modules ../../node_modules/.bin/jest --runInBand --forceExit --testPathPattern \"tests/integration/webhooks\" --ci --reporters=default --reporters=jest-junit",
|
|
||||||
"test:integration-security": "node --experimental-vm-modules ../../node_modules/.bin/jest --runInBand --forceExit --testPathPattern \"tests/integration/api/security\" --ci --reporters=default --reporters=jest-junit",
|
|
||||||
"test:integration-global-config": "node --experimental-vm-modules ../../node_modules/.bin/jest --runInBand --forceExit --testPathPattern \"tests/integration/api/global-config\" --ci --reporters=default --reporters=jest-junit",
|
|
||||||
"test:integration-participants": "node --experimental-vm-modules ../../node_modules/.bin/jest --runInBand --forceExit --testPathPattern \"tests/integration/api/participants\" --ci --reporters=default --reporters=jest-junit",
|
|
||||||
"test:integration-meetings": "node --experimental-vm-modules ../../node_modules/.bin/jest --runInBand --forceExit --testPathPattern \"tests/integration/api/meetings\" --ci --reporters=default --reporters=jest-junit",
|
"test:integration-meetings": "node --experimental-vm-modules ../../node_modules/.bin/jest --runInBand --forceExit --testPathPattern \"tests/integration/api/meetings\" --ci --reporters=default --reporters=jest-junit",
|
||||||
|
"test:integration-webhooks": "node --experimental-vm-modules ../../node_modules/.bin/jest --runInBand --forceExit --testPathPattern \"tests/integration/webhooks\" --ci --reporters=default --reporters=jest-junit",
|
||||||
|
"test:integration-auth-security": "node --experimental-vm-modules ../../node_modules/.bin/jest --runInBand --forceExit --testPathPattern \"tests/integration/api/(security|auth|api-keys|users)\" --ci --reporters=default --reporters=jest-junit",
|
||||||
|
"test:integration-security": "node --experimental-vm-modules ../../node_modules/.bin/jest --runInBand --forceExit --testPathPattern \"tests/integration/api/security\" --ci --reporters=default --reporters=jest-junit",
|
||||||
|
"test:integration-auth": "node --experimental-vm-modules ../../node_modules/.bin/jest --runInBand --forceExit --testPathPattern \"tests/integration/api/auth\" --ci --reporters=default --reporters=jest-junit",
|
||||||
|
"test:integration-api-keys": "node --experimental-vm-modules ../../node_modules/.bin/jest --runInBand --forceExit --testPathPattern \"tests/integration/api/api-keys\" --ci --reporters=default --reporters=jest-junit",
|
||||||
"test:integration-users": "node --experimental-vm-modules ../../node_modules/.bin/jest --runInBand --forceExit --testPathPattern \"tests/integration/api/users\" --ci --reporters=default --reporters=jest-junit",
|
"test:integration-users": "node --experimental-vm-modules ../../node_modules/.bin/jest --runInBand --forceExit --testPathPattern \"tests/integration/api/users\" --ci --reporters=default --reporters=jest-junit",
|
||||||
|
"test:integration-config-analytics": "node --experimental-vm-modules ../../node_modules/.bin/jest --runInBand --forceExit --testPathPattern \"tests/integration/api/(global-config|analytics)\" --ci --reporters=default --reporters=jest-junit",
|
||||||
|
"test:integration-global-config": "node --experimental-vm-modules ../../node_modules/.bin/jest --runInBand --forceExit --testPathPattern \"tests/integration/api/global-config\" --ci --reporters=default --reporters=jest-junit",
|
||||||
|
"test:integration-analytics": "node --experimental-vm-modules ../../node_modules/.bin/jest --runInBand --forceExit --testPathPattern \"tests/integration/api/analytics\" --ci --reporters=default --reporters=jest-junit",
|
||||||
|
"test:integration-recordings": "node --experimental-vm-modules ../../node_modules/.bin/jest --maxWorkers=1 --maxConcurrency=1 --forceExit --testPathPattern \"tests/integration/api/recordings\" --ci --reporters=default --reporters=jest-junit",
|
||||||
"test:unit": "node --experimental-vm-modules ../../node_modules/.bin/jest --runInBand --forceExit --testPathPattern \"tests/unit\" --ci --reporters=default --reporters=jest-junit",
|
"test:unit": "node --experimental-vm-modules ../../node_modules/.bin/jest --runInBand --forceExit --testPathPattern \"tests/unit\" --ci --reporters=default --reporters=jest-junit",
|
||||||
"lint:fix": "eslint src --fix",
|
"lint:fix": "eslint src --fix",
|
||||||
"format:code": "prettier --ignore-path .gitignore --write '**/*.{ts,js,json,md}'"
|
"format:code": "prettier --ignore-path .gitignore --write '**/*.{ts,js,json,md}'",
|
||||||
|
"clean": "rm -rf node_modules dist public test-results"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@openvidu-meet/typings": "workspace:*",
|
|
||||||
"@aws-sdk/client-s3": "3.846.0",
|
"@aws-sdk/client-s3": "3.846.0",
|
||||||
"@azure/storage-blob": "12.27.0",
|
"@azure/storage-blob": "12.27.0",
|
||||||
"@google-cloud/storage": "7.17.1",
|
"@google-cloud/storage": "7.17.3",
|
||||||
|
"@openvidu-meet/typings": "workspace:*",
|
||||||
"@sesamecare-oss/redlock": "1.4.0",
|
"@sesamecare-oss/redlock": "1.4.0",
|
||||||
"archiver": "7.0.1",
|
"archiver": "7.0.1",
|
||||||
"bcrypt": "5.1.1",
|
"bcrypt": "5.1.1",
|
||||||
@ -59,14 +65,16 @@
|
|||||||
"chalk": "5.6.2",
|
"chalk": "5.6.2",
|
||||||
"cookie-parser": "1.4.7",
|
"cookie-parser": "1.4.7",
|
||||||
"cors": "2.8.5",
|
"cors": "2.8.5",
|
||||||
"cron": "4.3.3",
|
"cron": "4.3.5",
|
||||||
"dotenv": "16.6.1",
|
"dotenv": "16.6.1",
|
||||||
"express": "4.21.2",
|
"express": "4.21.2",
|
||||||
"express-rate-limit": "7.5.1",
|
"express-rate-limit": "7.5.1",
|
||||||
"inversify": "6.2.2",
|
"inversify": "6.2.2",
|
||||||
"ioredis": "5.6.1",
|
"ioredis": "5.6.1",
|
||||||
"jwt-decode": "4.0.0",
|
"jwt-decode": "4.0.0",
|
||||||
"livekit-server-sdk": "2.13.1",
|
"livekit-server-sdk": "2.13.3",
|
||||||
|
"lodash.merge": "4.6.2",
|
||||||
|
"mongoose": "8.19.4",
|
||||||
"ms": "2.1.3",
|
"ms": "2.1.3",
|
||||||
"uid": "2.0.2",
|
"uid": "2.0.2",
|
||||||
"winston": "3.18.3",
|
"winston": "3.18.3",
|
||||||
@ -74,14 +82,15 @@
|
|||||||
"zod": "3.25.76"
|
"zod": "3.25.76"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/archiver": "6.0.3",
|
"@types/archiver": "6.0.4",
|
||||||
"@types/bcrypt": "5.0.2",
|
"@types/bcrypt": "5.0.2",
|
||||||
"@types/cookie-parser": "1.4.9",
|
"@types/cookie-parser": "1.4.9",
|
||||||
"@types/cors": "2.8.19",
|
"@types/cors": "2.8.19",
|
||||||
"@types/express": "4.17.23",
|
"@types/express": "4.17.25",
|
||||||
"@types/jest": "29.5.14",
|
"@types/jest": "29.5.14",
|
||||||
|
"@types/lodash.merge": "4.6.9",
|
||||||
"@types/ms": "2.1.0",
|
"@types/ms": "2.1.0",
|
||||||
"@types/node": "22.16.4",
|
"@types/node": "22.16.5",
|
||||||
"@types/supertest": "6.0.3",
|
"@types/supertest": "6.0.3",
|
||||||
"@types/unzipper": "0.10.11",
|
"@types/unzipper": "0.10.11",
|
||||||
"@types/validator": "13.15.2",
|
"@types/validator": "13.15.2",
|
||||||
@ -90,7 +99,7 @@
|
|||||||
"@typescript-eslint/parser": "6.21.0",
|
"@typescript-eslint/parser": "6.21.0",
|
||||||
"cross-env": "7.0.3",
|
"cross-env": "7.0.3",
|
||||||
"eslint": "8.57.1",
|
"eslint": "8.57.1",
|
||||||
"eslint-config-prettier": "9.1.0",
|
"eslint-config-prettier": "9.1.2",
|
||||||
"jest": "29.7.0",
|
"jest": "29.7.0",
|
||||||
"jest-fetch-mock": "3.0.3",
|
"jest-fetch-mock": "3.0.3",
|
||||||
"jest-junit": "16.0.0",
|
"jest-junit": "16.0.0",
|
||||||
@ -100,7 +109,7 @@
|
|||||||
"supertest": "7.1.3",
|
"supertest": "7.1.3",
|
||||||
"ts-jest": "29.4.0",
|
"ts-jest": "29.4.0",
|
||||||
"ts-jest-resolver": "2.0.1",
|
"ts-jest-resolver": "2.0.1",
|
||||||
"tsx": "4.20.3",
|
"tsx": "4.20.6",
|
||||||
"typescript": "5.9.2",
|
"typescript": "5.9.2",
|
||||||
"unzipper": "0.12.3"
|
"unzipper": "0.12.3"
|
||||||
},
|
},
|
||||||
|
|||||||
@ -1,14 +0,0 @@
|
|||||||
import { ParticipantRole, User } from '@openvidu-meet/typings';
|
|
||||||
import { ClaimGrants } from 'livekit-server-sdk';
|
|
||||||
|
|
||||||
// Override the Express Request type to include a session object with user and token properties
|
|
||||||
// This will allow controllers to access the user and token information from the request object in a type-safe manner
|
|
||||||
declare module 'express' {
|
|
||||||
interface Request {
|
|
||||||
session?: {
|
|
||||||
user?: User;
|
|
||||||
tokenClaims?: ClaimGrants;
|
|
||||||
participantRole?: ParticipantRole;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,35 +1,60 @@
|
|||||||
import { Container } from 'inversify';
|
import { Container } from 'inversify';
|
||||||
import { MEET_BLOB_STORAGE_MODE } from '../environment.js';
|
import { MEET_ENV } from '../environment.js';
|
||||||
import {
|
|
||||||
ABSService,
|
import { ApiKeyRepository } from '../repositories/api-key.repository.js';
|
||||||
ABSStorageProvider,
|
import { BaseRepository } from '../repositories/base.repository.js';
|
||||||
AuthService,
|
import { GlobalConfigRepository } from '../repositories/global-config.repository.js';
|
||||||
DistributedEventService,
|
import { MigrationRepository } from '../repositories/migration.repository.js';
|
||||||
FrontendEventService,
|
import { RecordingRepository } from '../repositories/recording.repository.js';
|
||||||
GCSService,
|
import { RoomRepository } from '../repositories/room.repository.js';
|
||||||
GCSStorageProvider,
|
import { UserRepository } from '../repositories/user.repository.js';
|
||||||
HttpContextService,
|
|
||||||
LiveKitService,
|
/*
|
||||||
LivekitWebhookService,
|
* Services should be imported in order of use, starting with services
|
||||||
LoggerService,
|
* without dependencies and then the services that depend on others. This
|
||||||
MeetStorageService,
|
* helps avoid dependency cycles and ensures constructors receive the
|
||||||
MutexService,
|
* dependencies already registered in the container.
|
||||||
OpenViduWebhookService,
|
*/
|
||||||
ParticipantNameService,
|
import { LoggerService } from '../services/logger.service.js';
|
||||||
ParticipantService,
|
import { RedisService } from '../services/redis.service.js';
|
||||||
RecordingService,
|
import { DistributedEventService } from '../services/distributed-event.service.js';
|
||||||
RedisService,
|
import { MutexService } from '../services/mutex.service.js';
|
||||||
RoomService,
|
import { TaskSchedulerService } from '../services/task-scheduler.service.js';
|
||||||
S3KeyBuilder,
|
import { BaseUrlService } from '../services/base-url.service.js';
|
||||||
S3Service,
|
import { RequestSessionService } from '../services/request-session.service.js';
|
||||||
S3StorageProvider,
|
|
||||||
StorageFactory,
|
import { TokenService } from '../services/token.service.js';
|
||||||
StorageKeyBuilder,
|
import { UserService } from '../services/user.service.js';
|
||||||
StorageProvider,
|
import { ApiKeyService } from '../services/api-key.service.js';
|
||||||
TaskSchedulerService,
|
import { GlobalConfigService } from '../services/global-config.service.js';
|
||||||
TokenService,
|
|
||||||
UserService
|
import { S3Service } from '../services/storage/providers/s3/s3.service.js';
|
||||||
} from '../services/index.js';
|
import { S3KeyBuilder } from '../services/storage/providers/s3/s3-storage-key.builder.js';
|
||||||
|
import { S3StorageProvider } from '../services/storage/providers/s3/s3-storage.provider.js';
|
||||||
|
import { ABSService } from '../services/storage/providers/abs/abs.service.js';
|
||||||
|
import { ABSStorageProvider } from '../services/storage/providers/abs/abs-storage.provider.js';
|
||||||
|
import { GCSService } from '../services/storage/providers/gcp/gcs.service.js';
|
||||||
|
import { GCSStorageProvider } from '../services/storage/providers/gcp/gcs-storage.provider.js';
|
||||||
|
|
||||||
|
import { MongoDBService } from '../services/storage/mongodb.service.js';
|
||||||
|
import { StorageInitService } from '../services/storage/storage-init.service.js';
|
||||||
|
import { StorageKeyBuilder, StorageProvider } from '../services/storage/storage.interface.js';
|
||||||
|
import { StorageFactory } from '../services/storage/storage.factory.js';
|
||||||
|
import { BlobStorageService } from '../services/storage/blob-storage.service.js';
|
||||||
|
|
||||||
|
import { MigrationService } from '../services/migration.service.js';
|
||||||
|
import { LiveKitService } from '../services/livekit.service.js';
|
||||||
|
import { FrontendEventService } from '../services/frontend-event.service.js';
|
||||||
|
import { RecordingService } from '../services/recording.service.js';
|
||||||
|
import { RoomService } from '../services/room.service.js';
|
||||||
|
import { ParticipantNameService } from '../services/participant-name.service.js';
|
||||||
|
import { RoomMemberService } from '../services/room-member.service.js';
|
||||||
|
import { OpenViduWebhookService } from '../services/openvidu-webhook.service.js';
|
||||||
|
import { LivekitWebhookService } from '../services/livekit-webhook.service.js';
|
||||||
|
import { RoomScheduledTasksService } from '../services/room-scheduled-tasks.service.js';
|
||||||
|
import { RecordingScheduledTasksService } from '../services/recording-scheduled-tasks.service.js';
|
||||||
|
import { AnalyticsService } from '../services/analytics.service.js';
|
||||||
|
import { AiAssistantService } from '../services/ai-assistant.service.js';
|
||||||
|
|
||||||
export const container: Container = new Container();
|
export const container: Container = new Container();
|
||||||
|
|
||||||
@ -53,24 +78,43 @@ export const registerDependencies = () => {
|
|||||||
container.bind(DistributedEventService).toSelf().inSingletonScope();
|
container.bind(DistributedEventService).toSelf().inSingletonScope();
|
||||||
container.bind(MutexService).toSelf().inSingletonScope();
|
container.bind(MutexService).toSelf().inSingletonScope();
|
||||||
container.bind(TaskSchedulerService).toSelf().inSingletonScope();
|
container.bind(TaskSchedulerService).toSelf().inSingletonScope();
|
||||||
container.bind(HttpContextService).toSelf().inSingletonScope();
|
container.bind(BaseUrlService).toSelf().inSingletonScope();
|
||||||
|
// RequestSessionService uses AsyncLocalStorage for request isolation
|
||||||
|
// It's a singleton but provides per-request data isolation automatically
|
||||||
|
container.bind(RequestSessionService).toSelf().inSingletonScope();
|
||||||
|
|
||||||
configureStorage(MEET_BLOB_STORAGE_MODE);
|
container.bind(MongoDBService).toSelf().inSingletonScope();
|
||||||
container.bind(StorageFactory).toSelf().inSingletonScope();
|
container.bind(BaseRepository).toSelf().inSingletonScope();
|
||||||
container.bind(MeetStorageService).toSelf().inSingletonScope();
|
container.bind(RoomRepository).toSelf().inSingletonScope();
|
||||||
|
container.bind(UserRepository).toSelf().inSingletonScope();
|
||||||
|
container.bind(ApiKeyRepository).toSelf().inSingletonScope();
|
||||||
|
container.bind(GlobalConfigRepository).toSelf().inSingletonScope();
|
||||||
|
container.bind(RecordingRepository).toSelf().inSingletonScope();
|
||||||
|
container.bind(MigrationRepository).toSelf().inSingletonScope();
|
||||||
|
|
||||||
container.bind(TokenService).toSelf().inSingletonScope();
|
container.bind(TokenService).toSelf().inSingletonScope();
|
||||||
container.bind(UserService).toSelf().inSingletonScope();
|
container.bind(UserService).toSelf().inSingletonScope();
|
||||||
container.bind(AuthService).toSelf().inSingletonScope();
|
container.bind(ApiKeyService).toSelf().inSingletonScope();
|
||||||
|
container.bind(GlobalConfigService).toSelf().inSingletonScope();
|
||||||
|
|
||||||
|
configureStorage(MEET_ENV.BLOB_STORAGE_MODE);
|
||||||
|
container.bind(StorageFactory).toSelf().inSingletonScope();
|
||||||
|
container.bind(BlobStorageService).toSelf().inSingletonScope();
|
||||||
|
container.bind(StorageInitService).toSelf().inSingletonScope();
|
||||||
|
container.bind(MigrationService).toSelf().inSingletonScope();
|
||||||
|
|
||||||
container.bind(FrontendEventService).toSelf().inSingletonScope();
|
container.bind(FrontendEventService).toSelf().inSingletonScope();
|
||||||
container.bind(LiveKitService).toSelf().inSingletonScope();
|
container.bind(LiveKitService).toSelf().inSingletonScope();
|
||||||
container.bind(RecordingService).toSelf().inSingletonScope();
|
container.bind(RecordingService).toSelf().inSingletonScope();
|
||||||
container.bind(RoomService).toSelf().inSingletonScope();
|
container.bind(RoomService).toSelf().inSingletonScope();
|
||||||
container.bind(ParticipantNameService).toSelf().inSingletonScope();
|
container.bind(ParticipantNameService).toSelf().inSingletonScope();
|
||||||
container.bind(ParticipantService).toSelf().inSingletonScope();
|
container.bind(RoomMemberService).toSelf().inSingletonScope();
|
||||||
container.bind(OpenViduWebhookService).toSelf().inSingletonScope();
|
container.bind(OpenViduWebhookService).toSelf().inSingletonScope();
|
||||||
container.bind(LivekitWebhookService).toSelf().inSingletonScope();
|
container.bind(LivekitWebhookService).toSelf().inSingletonScope();
|
||||||
|
container.bind(RoomScheduledTasksService).toSelf().inSingletonScope();
|
||||||
|
container.bind(RecordingScheduledTasksService).toSelf().inSingletonScope();
|
||||||
|
container.bind(AnalyticsService).toSelf().inSingletonScope();
|
||||||
|
container.bind(AiAssistantService).toSelf().inSingletonScope();
|
||||||
};
|
};
|
||||||
|
|
||||||
const configureStorage = (storageMode: string) => {
|
const configureStorage = (storageMode: string) => {
|
||||||
@ -100,13 +144,24 @@ const configureStorage = (storageMode: string) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export const initializeEagerServices = async () => {
|
export const initializeEagerServices = async () => {
|
||||||
// Force the creation of services that need to be initialized at startup
|
// Connect to MongoDB and check health
|
||||||
container.get(RecordingService);
|
const mongoService = container.get(MongoDBService);
|
||||||
|
await mongoService.connect();
|
||||||
|
await mongoService.checkHealth();
|
||||||
|
|
||||||
// Perform comprehensive health checks before initializing other services
|
// Perform blob storage health check
|
||||||
const storageService = container.get(MeetStorageService);
|
const blobStorageService = container.get(BlobStorageService);
|
||||||
await storageService.checkStartupHealth();
|
await blobStorageService.checkHealth();
|
||||||
|
|
||||||
// Initialize storage after health checks pass
|
// Run migrations
|
||||||
await storageService.initializeStorage();
|
const migrationService = container.get(MigrationService);
|
||||||
|
await migrationService.runMigrations();
|
||||||
|
|
||||||
|
// Initialize storage
|
||||||
|
const storageInitService = container.get(StorageInitService);
|
||||||
|
await storageInitService.initializeStorage();
|
||||||
|
|
||||||
|
// Initialize scheduled tasks services to register their cron jobs
|
||||||
|
container.get(RecordingScheduledTasksService);
|
||||||
|
container.get(RoomScheduledTasksService);
|
||||||
};
|
};
|
||||||
|
|||||||
@ -1,33 +1,21 @@
|
|||||||
import { StringValue } from 'ms';
|
import { StringValue } from 'ms';
|
||||||
|
import { SchemaVersion } from '../models/migration.model.js';
|
||||||
|
|
||||||
export const INTERNAL_CONFIG = {
|
export const INTERNAL_CONFIG = {
|
||||||
// Base paths for the API
|
// Base paths for the API
|
||||||
API_BASE_PATH_V1: '/api/v1',
|
API_BASE_PATH_V1: '/api/v1',
|
||||||
INTERNAL_API_BASE_PATH_V1: '/internal-api/v1',
|
INTERNAL_API_BASE_PATH_V1: '/internal-api/v1',
|
||||||
|
|
||||||
// Cookie names
|
|
||||||
ACCESS_TOKEN_COOKIE_NAME: 'OvMeetAccessToken',
|
|
||||||
REFRESH_TOKEN_COOKIE_NAME: 'OvMeetRefreshToken',
|
|
||||||
PARTICIPANT_TOKEN_COOKIE_NAME: 'OvMeetParticipantToken',
|
|
||||||
RECORDING_TOKEN_COOKIE_NAME: 'OvMeetRecordingToken',
|
|
||||||
|
|
||||||
// Headers names
|
// Headers names
|
||||||
API_KEY_HEADER: 'x-api-key',
|
API_KEY_HEADER: 'x-api-key',
|
||||||
ACCESS_TOKEN_HEADER: 'authorization',
|
ACCESS_TOKEN_HEADER: 'authorization',
|
||||||
REFRESH_TOKEN_HEADER: 'x-refresh-token',
|
REFRESH_TOKEN_HEADER: 'x-refresh-token',
|
||||||
PARTICIPANT_TOKEN_HEADER: 'x-participant-token',
|
ROOM_MEMBER_TOKEN_HEADER: 'x-room-member-token',
|
||||||
PARTICIPANT_ROLE_HEADER: 'x-participant-role',
|
|
||||||
RECORDING_TOKEN_HEADER: 'x-recording-token',
|
|
||||||
|
|
||||||
// Token expiration times
|
// Token expiration times
|
||||||
ACCESS_TOKEN_EXPIRATION: '2h',
|
ACCESS_TOKEN_EXPIRATION: '2h',
|
||||||
REFRESH_TOKEN_EXPIRATION: '1d',
|
REFRESH_TOKEN_EXPIRATION: '1d',
|
||||||
PARTICIPANT_TOKEN_EXPIRATION: '2h',
|
ROOM_MEMBER_TOKEN_EXPIRATION: '2h',
|
||||||
RECORDING_TOKEN_EXPIRATION: '2h',
|
|
||||||
|
|
||||||
// Participant name reservations
|
|
||||||
PARTICIPANT_MAX_CONCURRENT_NAME_REQUESTS: '20', // Maximum number of request by the same name at the same time allowed
|
|
||||||
PARTICIPANT_NAME_RESERVATION_TTL: '12h' as StringValue, // Time-to-live for participant name reservations
|
|
||||||
|
|
||||||
// Authentication usernames
|
// Authentication usernames
|
||||||
ANONYMOUS_USER: 'anonymous',
|
ANONYMOUS_USER: 'anonymous',
|
||||||
@ -36,25 +24,43 @@ export const INTERNAL_CONFIG = {
|
|||||||
// S3 configuration
|
// S3 configuration
|
||||||
S3_MAX_RETRIES_ATTEMPTS_ON_SAVE_ERROR: '5',
|
S3_MAX_RETRIES_ATTEMPTS_ON_SAVE_ERROR: '5',
|
||||||
S3_INITIAL_RETRY_DELAY_MS: '100',
|
S3_INITIAL_RETRY_DELAY_MS: '100',
|
||||||
S3_ROOMS_PREFIX: 'rooms',
|
|
||||||
S3_RECORDINGS_PREFIX: 'recordings',
|
|
||||||
S3_USERS_PREFIX: 'users',
|
|
||||||
S3_API_KEYS_PREFIX: 'api_keys',
|
|
||||||
|
|
||||||
// Garbage collection and recording intervals
|
// Cron job configuration
|
||||||
ROOM_GC_INTERVAL: '1h' as StringValue, // e.g. garbage collector interval for rooms
|
CRON_JOB_LOCK_TTL: '59s' as StringValue, // Default TTL for cron job locks to avoid overlapping executions
|
||||||
RECORDING_LOCK_TTL: '6h' as StringValue, // TTL for recording lock in Redis
|
|
||||||
RECORDING_STARTED_TIMEOUT: '20s' as StringValue, // Timeout for recording start
|
// Timing and cleanup settings for room lifecycle management
|
||||||
RECORDING_LOCK_GC_INTERVAL: '30m' as StringValue, // Garbage collection interval for recording locks
|
ROOM_EXPIRED_GC_INTERVAL: '1h' as StringValue, // Interval for processing and deleting expired rooms
|
||||||
RECORDING_ORPHANED_LOCK_GRACE_PERIOD: '1m' as StringValue, // Grace period for orphaned recording locks
|
ROOM_ACTIVE_VERIFICATION_GC_INTERVAL: '15m' as StringValue, // Interval for checking room 'active_meeting' status consistency
|
||||||
RECORDING_STALE_CLEANUP_INTERVAL: '15m' as StringValue, // Cleanup interval for stale recordings
|
|
||||||
RECORDING_STALE_AFTER: '5m' as StringValue, // Maximum allowed time since the last recording update before marking as stale
|
// Timing and cleanup settings for recording lifecycle management
|
||||||
|
RECORDING_STARTED_TIMEOUT: '20s' as StringValue, // Timeout for recording to be marked as started
|
||||||
|
RECORDING_ACTIVE_LOCK_TTL: '7d' as StringValue, // Redis Lock TTL for active recording in a room
|
||||||
|
RECORDING_ACTIVE_LOCK_GC_INTERVAL: '15m' as StringValue, // Interval for cleaning up stale active recording locks
|
||||||
|
RECORDING_ORPHANED_ACTIVE_LOCK_GRACE_PERIOD: '30s' as StringValue, // Grace period to consider an active recording lock as orphaned (should be greater than RECORDING_STARTED_TIMEOUT)
|
||||||
|
RECORDING_STALE_GC_INTERVAL: '14m' as StringValue, // Interval for cleaning up stale recordings (not updated recently)
|
||||||
|
RECORDING_STALE_GRACE_PERIOD: '5m' as StringValue, // Maximum allowed time since the last recording update before marking it as stale
|
||||||
|
|
||||||
CRON_JOB_MIN_LOCK_TTL: '59s' as StringValue, // Minimum TTL for cron job locks
|
|
||||||
// Additional intervals
|
// Additional intervals
|
||||||
MIN_FUTURE_TIME_FOR_ROOM_AUTODELETION_DATE: '1h' as StringValue, // Minimum time for room auto-deletion date
|
MIN_ROOM_AUTO_DELETE_DURATION: '1h' as StringValue, // Minimum duration before a room can be auto-deleted
|
||||||
MEETING_EMPTY_TIMEOUT: '' as StringValue, // Seconds to keep the meeting (LK room) open until the first participant joins
|
MEETING_EMPTY_TIMEOUT: (process.env.MEETING_EMPTY_TIMEOUT || '20s') as StringValue, // Seconds to keep the meeting (LK room) open until the first participant joins
|
||||||
MEETING_DEPARTURE_TIMEOUT: '' as StringValue // Seconds to keep the meeting (LK room) open after the last participant leaves
|
MEETING_DEPARTURE_TIMEOUT: (process.env.MEETING_DEPARTURE_TIMEOUT || '20s') as StringValue, // Seconds to keep the meeting (LK room) open after the last participant leaves
|
||||||
|
|
||||||
|
// Participant name reservation
|
||||||
|
PARTICIPANT_MAX_CONCURRENT_NAME_REQUESTS: '20', // Maximum number of request by the same name at the same time allowed
|
||||||
|
PARTICIPANT_NAME_RESERVATION_TTL: '12h' as StringValue, // Time-to-live for participant name reservations
|
||||||
|
|
||||||
|
CAPTIONS_AGENT_NAME: 'speech-processing',
|
||||||
|
|
||||||
|
// MongoDB Schema Versions
|
||||||
|
// These define the current schema version for each collection
|
||||||
|
// Increment when making breaking changes to the schema structure
|
||||||
|
// IMPORTANT: whenever you increment a schema version, update the MIGRATION_REV timestamp too.
|
||||||
|
// This helps surface merge conflicts when multiple branches create schema migrations concurrently.
|
||||||
|
GLOBAL_CONFIG_SCHEMA_VERSION: 1 as SchemaVersion, // MIGRATION_REV: 1771328577054
|
||||||
|
USER_SCHEMA_VERSION: 1 as SchemaVersion, // MIGRATION_REV: 1771328577054
|
||||||
|
API_KEY_SCHEMA_VERSION: 1 as SchemaVersion, // MIGRATION_REV: 1771328577054
|
||||||
|
ROOM_SCHEMA_VERSION: 2 as SchemaVersion, // MIGRATION_REV: 1771328577054
|
||||||
|
RECORDING_SCHEMA_VERSION: 2 as SchemaVersion // MIGRATION_REV: 1771328577054
|
||||||
};
|
};
|
||||||
|
|
||||||
// This function is used to set private configuration values for testing purposes.
|
// This function is used to set private configuration values for testing purposes.
|
||||||
|
|||||||
69
meet-ce/backend/src/controllers/ai-assistant.controller.ts
Normal file
69
meet-ce/backend/src/controllers/ai-assistant.controller.ts
Normal file
@ -0,0 +1,69 @@
|
|||||||
|
import { Request, Response } from 'express';
|
||||||
|
import { container } from '../config/dependency-injector.config.js';
|
||||||
|
import { handleError } from '../models/error.model.js';
|
||||||
|
import { AiAssistantService } from '../services/ai-assistant.service.js';
|
||||||
|
import { LoggerService } from '../services/logger.service.js';
|
||||||
|
import { RequestSessionService } from '../services/request-session.service.js';
|
||||||
|
import { TokenService } from '../services/token.service.js';
|
||||||
|
import { getRoomMemberToken } from '../utils/token.utils.js';
|
||||||
|
|
||||||
|
const getRoomMemberIdentityFromRequest = async (req: Request): Promise<string> => {
|
||||||
|
const tokenService = container.get(TokenService);
|
||||||
|
const token = getRoomMemberToken(req);
|
||||||
|
|
||||||
|
if (!token) {
|
||||||
|
throw new Error('Room member token not found');
|
||||||
|
}
|
||||||
|
|
||||||
|
const claims = await tokenService.verifyToken(token);
|
||||||
|
|
||||||
|
if (!claims.sub) {
|
||||||
|
throw new Error('Room member token does not include participant identity');
|
||||||
|
}
|
||||||
|
|
||||||
|
return claims.sub;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const createAssistant = async (req: Request, res: Response) => {
|
||||||
|
const logger = container.get(LoggerService);
|
||||||
|
const requestSessionService = container.get(RequestSessionService);
|
||||||
|
const aiAssistantService = container.get(AiAssistantService);
|
||||||
|
// const payload: MeetCreateAssistantRequest = req.body;
|
||||||
|
const roomId = requestSessionService.getRoomIdFromToken();
|
||||||
|
|
||||||
|
if (!roomId) {
|
||||||
|
return handleError(res, new Error('Could not resolve room from token'), 'creating assistant');
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
const participantIdentity = await getRoomMemberIdentityFromRequest(req);
|
||||||
|
logger.verbose(`Creating assistant for participant '${participantIdentity}' in room '${roomId}'`);
|
||||||
|
const assistant = await aiAssistantService.createLiveCaptionsAssistant(roomId, participantIdentity);
|
||||||
|
return res.status(200).json(assistant);
|
||||||
|
} catch (error) {
|
||||||
|
handleError(res, error, `creating assistant in room '${roomId}'`);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export const cancelAssistant = async (req: Request, res: Response) => {
|
||||||
|
const logger = container.get(LoggerService);
|
||||||
|
const requestSessionService = container.get(RequestSessionService);
|
||||||
|
const aiAssistantService = container.get(AiAssistantService);
|
||||||
|
const { assistantId } = req.params;
|
||||||
|
const roomId = requestSessionService.getRoomIdFromToken();
|
||||||
|
|
||||||
|
if (!roomId) {
|
||||||
|
return handleError(res, new Error('Could not resolve room from token'), 'canceling assistant');
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
const participantIdentity = await getRoomMemberIdentityFromRequest(req);
|
||||||
|
logger.verbose(
|
||||||
|
`Canceling assistant '${assistantId}' for participant '${participantIdentity}' in room '${roomId}'`
|
||||||
|
);
|
||||||
|
await aiAssistantService.cancelAssistant(assistantId, roomId, participantIdentity);
|
||||||
|
return res.status(204).send();
|
||||||
|
} catch (error) {
|
||||||
|
handleError(res, error, `canceling assistant '${assistantId}' in room '${roomId}'`);
|
||||||
|
}
|
||||||
|
};
|
||||||
18
meet-ce/backend/src/controllers/analytics.controller.ts
Normal file
18
meet-ce/backend/src/controllers/analytics.controller.ts
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
import { Request, Response } from 'express';
|
||||||
|
import { container } from '../config/dependency-injector.config.js';
|
||||||
|
import { handleError } from '../models/error.model.js';
|
||||||
|
import { AnalyticsService } from '../services/analytics.service.js';
|
||||||
|
import { LoggerService } from '../services/logger.service.js';
|
||||||
|
|
||||||
|
export const getAnalytics = async (req: Request, res: Response): Promise<void> => {
|
||||||
|
const logger = container.get(LoggerService);
|
||||||
|
logger.verbose('Analytics request received');
|
||||||
|
|
||||||
|
try {
|
||||||
|
const analyticsService = container.get(AnalyticsService);
|
||||||
|
const analytics = await analyticsService.getAnalytics();
|
||||||
|
res.status(200).json(analytics);
|
||||||
|
} catch (error) {
|
||||||
|
handleError(res, error, 'getting analytics');
|
||||||
|
}
|
||||||
|
};
|
||||||
47
meet-ce/backend/src/controllers/api-key.controller.ts
Normal file
47
meet-ce/backend/src/controllers/api-key.controller.ts
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
import { Request, Response } from 'express';
|
||||||
|
import { container } from '../config/dependency-injector.config.js';
|
||||||
|
import { handleError } from '../models/error.model.js';
|
||||||
|
import { ApiKeyService } from '../services/api-key.service.js';
|
||||||
|
import { LoggerService } from '../services/logger.service.js';
|
||||||
|
|
||||||
|
export const createApiKey = async (_req: Request, res: Response) => {
|
||||||
|
const logger = container.get(LoggerService);
|
||||||
|
logger.verbose('Create API key request received');
|
||||||
|
|
||||||
|
const apiKeyService = container.get(ApiKeyService);
|
||||||
|
|
||||||
|
try {
|
||||||
|
const apiKey = await apiKeyService.createApiKey();
|
||||||
|
return res.status(201).json(apiKey);
|
||||||
|
} catch (error) {
|
||||||
|
handleError(res, error, 'creating API key');
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export const getApiKeys = async (_req: Request, res: Response) => {
|
||||||
|
const logger = container.get(LoggerService);
|
||||||
|
logger.verbose('Get API keys request received');
|
||||||
|
|
||||||
|
const apiKeyService = container.get(ApiKeyService);
|
||||||
|
|
||||||
|
try {
|
||||||
|
const apiKeys = await apiKeyService.getApiKeys();
|
||||||
|
return res.status(200).json(apiKeys);
|
||||||
|
} catch (error) {
|
||||||
|
handleError(res, error, 'getting API keys');
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export const deleteApiKeys = async (_req: Request, res: Response) => {
|
||||||
|
const logger = container.get(LoggerService);
|
||||||
|
logger.verbose('Delete API keys request received');
|
||||||
|
|
||||||
|
const apiKeyService = container.get(ApiKeyService);
|
||||||
|
|
||||||
|
try {
|
||||||
|
await apiKeyService.deleteApiKeys();
|
||||||
|
return res.status(200).json({ message: 'API keys deleted successfully' });
|
||||||
|
} catch (error) {
|
||||||
|
handleError(res, error, 'deleting API keys');
|
||||||
|
}
|
||||||
|
};
|
||||||
@ -1,8 +1,6 @@
|
|||||||
import { AuthTransportMode } from '@openvidu-meet/typings';
|
|
||||||
import { Request, Response } from 'express';
|
import { Request, Response } from 'express';
|
||||||
import { ClaimGrants } from 'livekit-server-sdk';
|
import { ClaimGrants } from 'livekit-server-sdk';
|
||||||
import { container } from '../config/index.js';
|
import { container } from '../config/dependency-injector.config.js';
|
||||||
import { INTERNAL_CONFIG } from '../config/internal-config.js';
|
|
||||||
import {
|
import {
|
||||||
errorInvalidCredentials,
|
errorInvalidCredentials,
|
||||||
errorInvalidRefreshToken,
|
errorInvalidRefreshToken,
|
||||||
@ -11,16 +9,18 @@ import {
|
|||||||
handleError,
|
handleError,
|
||||||
rejectRequestFromMeetError
|
rejectRequestFromMeetError
|
||||||
} from '../models/error.model.js';
|
} from '../models/error.model.js';
|
||||||
import { AuthService, LoggerService, TokenService, UserService } from '../services/index.js';
|
import { LoggerService } from '../services/logger.service.js';
|
||||||
import { getAuthTransportMode, getCookieOptions, getRefreshToken } from '../utils/index.js';
|
import { TokenService } from '../services/token.service.js';
|
||||||
|
import { UserService } from '../services/user.service.js';
|
||||||
|
import { getRefreshToken } from '../utils/token.utils.js';
|
||||||
|
|
||||||
export const login = async (req: Request, res: Response) => {
|
export const login = async (req: Request, res: Response) => {
|
||||||
const logger = container.get(LoggerService);
|
const logger = container.get(LoggerService);
|
||||||
logger.verbose('Login request received');
|
logger.verbose('Login request received');
|
||||||
const { username, password } = req.body as { username: string; password: string };
|
const { username, password } = req.body as { username: string; password: string };
|
||||||
|
|
||||||
const authService = container.get(AuthService);
|
const userService = container.get(UserService);
|
||||||
const user = await authService.authenticateUser(username, password);
|
const user = await userService.authenticateUser(username, password);
|
||||||
|
|
||||||
if (!user) {
|
if (!user) {
|
||||||
logger.warn('Login failed');
|
logger.warn('Login failed');
|
||||||
@ -34,49 +34,19 @@ export const login = async (req: Request, res: Response) => {
|
|||||||
const refreshToken = await tokenService.generateRefreshToken(user);
|
const refreshToken = await tokenService.generateRefreshToken(user);
|
||||||
|
|
||||||
logger.info(`Login succeeded for user '${username}'`);
|
logger.info(`Login succeeded for user '${username}'`);
|
||||||
const transportMode = await getAuthTransportMode();
|
return res.status(200).json({
|
||||||
|
message: `User '${username}' logged in successfully`,
|
||||||
if (transportMode === AuthTransportMode.HEADER) {
|
accessToken,
|
||||||
// Send tokens in response body for header mode
|
refreshToken
|
||||||
return res.status(200).json({
|
});
|
||||||
message: `User '${username}' logged in successfully`,
|
|
||||||
accessToken,
|
|
||||||
refreshToken
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
// Send tokens as cookies for cookie mode
|
|
||||||
res.cookie(
|
|
||||||
INTERNAL_CONFIG.ACCESS_TOKEN_COOKIE_NAME,
|
|
||||||
accessToken,
|
|
||||||
getCookieOptions('/', INTERNAL_CONFIG.ACCESS_TOKEN_EXPIRATION)
|
|
||||||
);
|
|
||||||
res.cookie(
|
|
||||||
INTERNAL_CONFIG.REFRESH_TOKEN_COOKIE_NAME,
|
|
||||||
refreshToken,
|
|
||||||
getCookieOptions(
|
|
||||||
`${INTERNAL_CONFIG.INTERNAL_API_BASE_PATH_V1}/auth`,
|
|
||||||
INTERNAL_CONFIG.REFRESH_TOKEN_EXPIRATION
|
|
||||||
)
|
|
||||||
);
|
|
||||||
return res.status(200).json({ message: `User '${username}' logged in successfully` });
|
|
||||||
}
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
handleError(res, error, 'generating access and refresh tokens');
|
handleError(res, error, 'generating access and refresh tokens');
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
export const logout = async (_req: Request, res: Response) => {
|
export const logout = async (_req: Request, res: Response) => {
|
||||||
const transportMode = await getAuthTransportMode();
|
// The client is responsible for clearing tokens from localStorage,
|
||||||
|
// so just respond with success
|
||||||
if (transportMode === AuthTransportMode.COOKIE) {
|
|
||||||
// Clear cookies only in cookie mode
|
|
||||||
res.clearCookie(INTERNAL_CONFIG.ACCESS_TOKEN_COOKIE_NAME);
|
|
||||||
res.clearCookie(INTERNAL_CONFIG.REFRESH_TOKEN_COOKIE_NAME, {
|
|
||||||
path: `${INTERNAL_CONFIG.INTERNAL_API_BASE_PATH_V1}/auth`
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// In header mode, the client is responsible for clearing localStorage
|
|
||||||
return res.status(200).json({ message: 'Logout successful' });
|
return res.status(200).json({ message: 'Logout successful' });
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -118,66 +88,11 @@ export const refreshToken = async (req: Request, res: Response) => {
|
|||||||
const accessToken = await tokenService.generateAccessToken(user);
|
const accessToken = await tokenService.generateAccessToken(user);
|
||||||
|
|
||||||
logger.info(`Access token refreshed for user '${username}'`);
|
logger.info(`Access token refreshed for user '${username}'`);
|
||||||
const transportMode = await getAuthTransportMode();
|
return res.status(200).json({
|
||||||
|
message: `Access token for user '${username}' successfully refreshed`,
|
||||||
if (transportMode === AuthTransportMode.HEADER) {
|
accessToken
|
||||||
// Send access token in response body for header mode
|
});
|
||||||
return res.status(200).json({
|
|
||||||
message: `Access token for user '${username}' successfully refreshed`,
|
|
||||||
accessToken
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
// Send access token as cookie for cookie mode
|
|
||||||
res.cookie(
|
|
||||||
INTERNAL_CONFIG.ACCESS_TOKEN_COOKIE_NAME,
|
|
||||||
accessToken,
|
|
||||||
getCookieOptions('/', INTERNAL_CONFIG.ACCESS_TOKEN_EXPIRATION)
|
|
||||||
);
|
|
||||||
return res.status(200).json({ message: `Access token for user '${username}' successfully refreshed` });
|
|
||||||
}
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
handleError(res, error, 'refreshing token');
|
handleError(res, error, 'refreshing token');
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
export const createApiKey = async (_req: Request, res: Response) => {
|
|
||||||
const logger = container.get(LoggerService);
|
|
||||||
logger.verbose('Create API key request received');
|
|
||||||
|
|
||||||
const authService = container.get(AuthService);
|
|
||||||
|
|
||||||
try {
|
|
||||||
const apiKey = await authService.createApiKey();
|
|
||||||
return res.status(201).json(apiKey);
|
|
||||||
} catch (error) {
|
|
||||||
handleError(res, error, 'creating API key');
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
export const getApiKeys = async (_req: Request, res: Response) => {
|
|
||||||
const logger = container.get(LoggerService);
|
|
||||||
logger.verbose('Get API keys request received');
|
|
||||||
|
|
||||||
const authService = container.get(AuthService);
|
|
||||||
|
|
||||||
try {
|
|
||||||
const apiKeys = await authService.getApiKeys();
|
|
||||||
return res.status(200).json(apiKeys);
|
|
||||||
} catch (error) {
|
|
||||||
handleError(res, error, 'getting API keys');
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
export const deleteApiKeys = async (_req: Request, res: Response) => {
|
|
||||||
const logger = container.get(LoggerService);
|
|
||||||
logger.verbose('Delete API keys request received');
|
|
||||||
|
|
||||||
const authService = container.get(AuthService);
|
|
||||||
|
|
||||||
try {
|
|
||||||
await authService.deleteApiKeys();
|
|
||||||
return res.status(200).json({ message: 'API keys deleted successfully' });
|
|
||||||
} catch (error) {
|
|
||||||
handleError(res, error, 'deleting API keys');
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|||||||
124
meet-ce/backend/src/controllers/global-config.controller.ts
Normal file
124
meet-ce/backend/src/controllers/global-config.controller.ts
Normal file
@ -0,0 +1,124 @@
|
|||||||
|
import { MeetAppearanceConfig, SecurityConfig, WebhookConfig } from '@openvidu-meet/typings';
|
||||||
|
import { Request, Response } from 'express';
|
||||||
|
import { container } from '../config/dependency-injector.config.js';
|
||||||
|
import { MEET_ENV } from '../environment.js';
|
||||||
|
import { handleError } from '../models/error.model.js';
|
||||||
|
import { GlobalConfigService } from '../services/global-config.service.js';
|
||||||
|
import { LoggerService } from '../services/logger.service.js';
|
||||||
|
import { OpenViduWebhookService } from '../services/openvidu-webhook.service.js';
|
||||||
|
|
||||||
|
export const updateWebhookConfig = async (req: Request, res: Response) => {
|
||||||
|
const logger = container.get(LoggerService);
|
||||||
|
const configService = container.get(GlobalConfigService);
|
||||||
|
|
||||||
|
logger.info(`Updating webhooks config: ${JSON.stringify(req.body)}`);
|
||||||
|
const webhookConfig = req.body as WebhookConfig;
|
||||||
|
|
||||||
|
try {
|
||||||
|
await configService.updateWebhookConfig(webhookConfig);
|
||||||
|
return res.status(200).json({ message: 'Webhooks config updated successfully' });
|
||||||
|
} catch (error) {
|
||||||
|
handleError(res, error, 'updating webhooks config');
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export const getWebhookConfig = async (_req: Request, res: Response) => {
|
||||||
|
const logger = container.get(LoggerService);
|
||||||
|
const configService = container.get(GlobalConfigService);
|
||||||
|
|
||||||
|
logger.verbose('Getting webhooks config');
|
||||||
|
|
||||||
|
try {
|
||||||
|
const webhookConfig = await configService.getWebhookConfig();
|
||||||
|
return res.status(200).json(webhookConfig);
|
||||||
|
} catch (error) {
|
||||||
|
handleError(res, error, 'getting webhooks config');
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export const testWebhook = async (req: Request, res: Response) => {
|
||||||
|
const logger = container.get(LoggerService);
|
||||||
|
const webhookService = container.get(OpenViduWebhookService);
|
||||||
|
|
||||||
|
logger.verbose(`Testing webhook URL: ${req.body.url}`);
|
||||||
|
const url = req.body.url;
|
||||||
|
|
||||||
|
try {
|
||||||
|
await webhookService.testWebhookUrl(url);
|
||||||
|
logger.info(`Webhook URL '${url}' is valid`);
|
||||||
|
return res.status(200).json({ message: 'Webhook URL is valid' });
|
||||||
|
} catch (error) {
|
||||||
|
handleError(res, error, 'testing webhook URL');
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export const updateSecurityConfig = async (req: Request, res: Response) => {
|
||||||
|
const logger = container.get(LoggerService);
|
||||||
|
const configService = container.get(GlobalConfigService);
|
||||||
|
|
||||||
|
logger.verbose(`Updating security config: ${JSON.stringify(req.body)}`);
|
||||||
|
const securityConfig = req.body as SecurityConfig;
|
||||||
|
|
||||||
|
try {
|
||||||
|
await configService.updateSecurityConfig(securityConfig);
|
||||||
|
return res.status(200).json({ message: 'Security config updated successfully' });
|
||||||
|
} catch (error) {
|
||||||
|
handleError(res, error, 'updating security config');
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export const getSecurityConfig = async (_req: Request, res: Response) => {
|
||||||
|
const logger = container.get(LoggerService);
|
||||||
|
const configService = container.get(GlobalConfigService);
|
||||||
|
|
||||||
|
logger.verbose('Getting security config');
|
||||||
|
|
||||||
|
try {
|
||||||
|
const securityConfig = await configService.getSecurityConfig();
|
||||||
|
return res.status(200).json(securityConfig);
|
||||||
|
} catch (error) {
|
||||||
|
handleError(res, error, 'getting security config');
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export const updateRoomsAppearanceConfig = async (req: Request, res: Response) => {
|
||||||
|
const logger = container.get(LoggerService);
|
||||||
|
const configService = container.get(GlobalConfigService);
|
||||||
|
|
||||||
|
logger.verbose(`Updating rooms appearance config: ${JSON.stringify(req.body)}`);
|
||||||
|
const appearanceConfig = req.body as { appearance: MeetAppearanceConfig };
|
||||||
|
|
||||||
|
try {
|
||||||
|
await configService.updateRoomsAppearanceConfig(appearanceConfig);
|
||||||
|
return res.status(200).json({ message: 'Rooms appearance config updated successfully' });
|
||||||
|
} catch (error) {
|
||||||
|
handleError(res, error, 'updating rooms appearance config');
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export const getRoomsAppearanceConfig = async (_req: Request, res: Response) => {
|
||||||
|
const logger = container.get(LoggerService);
|
||||||
|
const configService = container.get(GlobalConfigService);
|
||||||
|
|
||||||
|
logger.verbose(`Getting rooms appearance config`);
|
||||||
|
|
||||||
|
try {
|
||||||
|
const appearanceConfig = await configService.getRoomsAppearanceConfig();
|
||||||
|
return res.status(200).json(appearanceConfig);
|
||||||
|
} catch (error) {
|
||||||
|
handleError(res, error, 'getting rooms appearance config');
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export const getCaptionsConfig = async (_req: Request, res: Response) => {
|
||||||
|
const logger = container.get(LoggerService);
|
||||||
|
|
||||||
|
logger.verbose('Getting captions config');
|
||||||
|
|
||||||
|
try {
|
||||||
|
const captionsEnabled = MEET_ENV.CAPTIONS_ENABLED === 'true';
|
||||||
|
return res.status(200).json({ enabled: captionsEnabled });
|
||||||
|
} catch (error) {
|
||||||
|
handleError(res, error, 'getting captions config');
|
||||||
|
}
|
||||||
|
};
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user