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
|
||||
**/*.mkv
|
||||
**/*.webm
|
||||
**/*.mp3
|
||||
**/*.wav
|
||||
**/*.flac
|
||||
|
||||
# ====================================================
|
||||
|
||||
@ -1,49 +0,0 @@
|
||||
# CONFIGURACIÓN PARA EASYPANEL + LIVEKIT SELF-HOSTED
|
||||
|
||||
# ===========================================
|
||||
# CONFIGURACIÓN BÁSICA
|
||||
# ===========================================
|
||||
NODE_ENV=production
|
||||
MEET_LOG_LEVEL=info
|
||||
MEET_BLOB_STORAGE_MODE=memory
|
||||
|
||||
# ===========================================
|
||||
# USUARIO ADMINISTRADOR
|
||||
# ===========================================
|
||||
ADMIN_USER=admin
|
||||
ADMIN_PASSWORD=admin123
|
||||
|
||||
# ===========================================
|
||||
# LIVEKIT SELF-HOSTED (RED LOCAL + PÚBLICO)
|
||||
# ===========================================
|
||||
# ✅ LiveKit corriendo en servidor dedicado
|
||||
# ✅ Puertos UDP expuestos en router
|
||||
# ✅ SSL/WSS configurado
|
||||
|
||||
# URL de tu LiveKit self-hosted (cambiar por tu dominio/IP)
|
||||
LIVEKIT_URL=wss://mi-livekit.duckdns.org
|
||||
# O con IP fija: wss://TU_IP_PUBLICA:443
|
||||
|
||||
LIVEKIT_API_KEY=production-key
|
||||
LIVEKIT_API_SECRET=tu-super-secret-de-32-caracteres-o-mas
|
||||
|
||||
# ===========================================
|
||||
# REDIS (OPCIONAL para EasyPanel)
|
||||
# ===========================================
|
||||
# Si quieres Redis también en EasyPanel
|
||||
# REDIS_HOST=redis
|
||||
# REDIS_PORT=6379
|
||||
# REDIS_PASSWORD=admin-redis
|
||||
|
||||
# ===========================================
|
||||
# ARQUITECTURA FINAL:
|
||||
# ===========================================
|
||||
# Internet → Router (Port Forward) → LiveKit Server (192.168.1.19)
|
||||
# ↘ EasyPanel → OpenVidu Meet Backend
|
||||
#
|
||||
# Ventajas:
|
||||
# ✅ Control total sobre LiveKit
|
||||
# ✅ Sin costos mensuales externos
|
||||
# ✅ Rendimiento en red local
|
||||
# ✅ Acceso desde internet
|
||||
# ✅ Escalable según hardware
|
||||
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:
|
||||
push:
|
||||
paths:
|
||||
- 'backend/src/**'
|
||||
- 'backend/package.json'
|
||||
- 'backend/pnpm-lock.yaml'
|
||||
- 'backend/tests/**'
|
||||
- 'meet-ce/backend/src/**'
|
||||
- 'meet-ce/backend/package.json'
|
||||
- 'pnpm-lock.yaml'
|
||||
- 'meet-ce/backend/tests/**'
|
||||
- '.github/workflows/backend-integration-test.yaml'
|
||||
pull_request:
|
||||
paths:
|
||||
- 'backend/src/**'
|
||||
- 'meet-ce/backend/src/**'
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
use-aws:
|
||||
@ -22,7 +22,7 @@ on:
|
||||
jobs:
|
||||
build-components:
|
||||
name: Build OpenVidu Components Angular
|
||||
runs-on: ov-actions-runner
|
||||
runs-on: ${{ vars.LABEL_WORKER_SELFHOSTED }}
|
||||
steps:
|
||||
- name: Build Components
|
||||
id: build
|
||||
@ -33,26 +33,19 @@ jobs:
|
||||
test-api:
|
||||
name: ${{ matrix.test-name }}
|
||||
needs: build-components
|
||||
runs-on: ov-actions-runner
|
||||
runs-on: ${{ vars.LABEL_WORKER_SELFHOSTED }}
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
include:
|
||||
- test-name: 'Rooms API Tests'
|
||||
test-script: 'test:integration-backend-rooms'
|
||||
- test-name: 'Room Management API Tests (Rooms, Meetings)'
|
||||
test-script: 'test:integration-backend-room-management'
|
||||
- test-name: 'Webhook Tests'
|
||||
test-script: 'test:integration-backend-webhooks'
|
||||
- test-name: 'Security API Tests'
|
||||
test-script: 'test:integration-backend-security'
|
||||
azure-container: 'openvidu-appdata-security'
|
||||
- test-name: 'Global Config API Tests'
|
||||
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'
|
||||
- test-name: 'Auth & Security API Tests (Security, Auth, API Keys, Users)'
|
||||
test-script: 'test:integration-backend-auth-security'
|
||||
- test-name: 'Config & Analytics API Tests (Global Config, Analytics)'
|
||||
test-script: 'test:integration-backend-config-analytics'
|
||||
steps:
|
||||
- name: Install LK CLI
|
||||
run: curl -sSL https://get.livekit.io/cli | bash
|
||||
@ -85,13 +78,11 @@ jobs:
|
||||
with:
|
||||
build_components_angular: 'true'
|
||||
components_artifact_name: ${{ needs.build-components.outputs.artifact_name }}
|
||||
env:
|
||||
MEET_AZURE_CONTAINER_NAME: ${{ matrix.azure-container || '' }}
|
||||
|
||||
- name: Run tests
|
||||
run: pnpm run ${{ matrix.test-script }}
|
||||
env:
|
||||
JEST_JUNIT_OUTPUT_DIR: './backend/reports/'
|
||||
JEST_JUNIT_OUTPUT_DIR: './meet-ce/backend/reports/'
|
||||
|
||||
- name: Upload OpenVidu Meet logs
|
||||
if: failure()
|
||||
@ -115,7 +106,7 @@ jobs:
|
||||
|
||||
start-aws-runner-s3:
|
||||
name: Prepare AWS runner (S3)
|
||||
runs-on: ov-actions-runner
|
||||
runs-on: ${{ vars.LABEL_WORKER_SELFHOSTED }}
|
||||
if: ${{ inputs.use-aws != 'false' }}
|
||||
outputs:
|
||||
label: ${{ steps.start-ec2-runner.outputs.label }}
|
||||
@ -138,7 +129,7 @@ jobs:
|
||||
|
||||
start-aws-runner-abs:
|
||||
name: Prepare AWS runner (ABS)
|
||||
runs-on: ov-actions-runner
|
||||
runs-on: ${{ vars.LABEL_WORKER_SELFHOSTED }}
|
||||
if: ${{ inputs.use-aws != 'false' }}
|
||||
outputs:
|
||||
label: ${{ steps.start-ec2-runner.outputs.label }}
|
||||
@ -161,7 +152,7 @@ jobs:
|
||||
|
||||
start-aws-runner-gcs:
|
||||
name: Prepare AWS runner (GCS)
|
||||
runs-on: ov-actions-runner
|
||||
runs-on: ${{ vars.LABEL_WORKER_SELFHOSTED }}
|
||||
if: ${{ inputs.use-aws != 'false' }}
|
||||
outputs:
|
||||
label: ${{ steps.start-ec2-runner.outputs.label }}
|
||||
@ -190,7 +181,7 @@ jobs:
|
||||
- start-aws-runner-gcs
|
||||
- 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') }}
|
||||
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:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
@ -292,7 +283,7 @@ jobs:
|
||||
- name: Run tests
|
||||
run: pnpm run test:integration-backend-recordings
|
||||
env:
|
||||
JEST_JUNIT_OUTPUT_DIR: './backend/reports/'
|
||||
JEST_JUNIT_OUTPUT_DIR: './meet-ce/backend/reports/'
|
||||
MEET_BLOB_STORAGE_MODE: ${{ matrix.storage-provider }}
|
||||
# ABS variables
|
||||
MEET_AZURE_ACCOUNT_NAME: ${{ matrix.storage-provider == 'abs' && vars.MEET_AZURE_ACCOUNT_NAME || '' }}
|
||||
@ -315,7 +306,7 @@ jobs:
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: recordings-test-${{ matrix.storage-provider }}-openvidu-meet-logs
|
||||
path: backend/meet_backend.log
|
||||
path: meet_backend.log
|
||||
retention-days: 2
|
||||
|
||||
- name: Clean up credentials
|
||||
@ -335,7 +326,7 @@ jobs:
|
||||
- start-aws-runner-abs
|
||||
- start-aws-runner-gcs
|
||||
- test-recordings
|
||||
runs-on: ov-actions-runner
|
||||
runs-on: ${{ vars.LABEL_WORKER_SELFHOSTED }}
|
||||
if: ${{ always() }}
|
||||
strategy:
|
||||
fail-fast: false
|
||||
@ -359,7 +350,7 @@ jobs:
|
||||
- build-components
|
||||
- test-api
|
||||
- test-recordings
|
||||
runs-on: ov-actions-runner
|
||||
runs-on: ${{ vars.LABEL_WORKER_SELFHOSTED }}
|
||||
if: ${{ always() }}
|
||||
steps:
|
||||
- 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:
|
||||
unit-test:
|
||||
name: Backend Unit Tests
|
||||
runs-on: ov-actions-runner
|
||||
runs-on: ${{ vars.LABEL_WORKER_SELFHOSTED }}
|
||||
steps:
|
||||
- name: Setup Node.js
|
||||
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:
|
||||
e2e-tests:
|
||||
name: WebComponent E2E Tests
|
||||
runs-on: ov-actions-runner
|
||||
runs-on: ${{ vars.LABEL_WORKER_SELFHOSTED }}
|
||||
steps:
|
||||
- name: Setup Node.js
|
||||
uses: actions/setup-node@v5
|
||||
@ -17,6 +17,8 @@ jobs:
|
||||
uses: pnpm/action-setup@v4
|
||||
with:
|
||||
version: 10.18.3
|
||||
- name: Install LK CLI
|
||||
run: curl -sSL https://get.livekit.io/cli | bash
|
||||
- name: Setup OpenVidu Local Deployment
|
||||
uses: OpenVidu/actions/start-openvidu-local-deployment@main
|
||||
with:
|
||||
@ -56,7 +58,7 @@ jobs:
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: room-test-results
|
||||
path: frontend/webcomponent/test-results/
|
||||
path: meet-ce/frontend/webcomponent/test-results/
|
||||
retention-days: 2
|
||||
|
||||
- name: Upload TestApp logs on failure
|
||||
@ -64,7 +66,7 @@ jobs:
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: room-test-testapp-logs
|
||||
path: testapp/testapp.log
|
||||
path: testapp.log
|
||||
retention-days: 2
|
||||
|
||||
- name: Upload OpenVidu Meet logs on failure
|
||||
@ -72,7 +74,7 @@ jobs:
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: room-test-openvidu-meet-logs
|
||||
path: backend/meet_backend.log
|
||||
path: meet_backend.log
|
||||
retention-days: 2
|
||||
|
||||
- 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:
|
||||
unit-test:
|
||||
name: WebComponent Unit Tests
|
||||
runs-on: ov-actions-runner
|
||||
runs-on: ${{ vars.LABEL_WORKER_SELFHOSTED }}
|
||||
steps:
|
||||
- name: Setup Node.js
|
||||
uses: actions/setup-node@v5
|
||||
|
||||
2
.gitignore
vendored
2
.gitignore
vendored
@ -37,7 +37,7 @@ pnpm-debug.log*
|
||||
|
||||
|
||||
**/**/test-results
|
||||
**/**/public/
|
||||
**/backend/public/
|
||||
|
||||
**/*/coverage
|
||||
**/**/test-results
|
||||
|
||||
13
.npmrc
13
.npmrc
@ -1,18 +1,10 @@
|
||||
# Docker/CI specific npm configuration
|
||||
# This configuration is used during Docker builds and CI workflows
|
||||
# to prevent linking workspace packages and use published versions instead
|
||||
|
||||
# Disable workspace package linking
|
||||
# This forces pnpm to install packages from registry or local tarballs
|
||||
link-workspace-packages=false
|
||||
|
||||
# Strict peer dependencies
|
||||
strict-peer-dependencies=false
|
||||
|
||||
# Auto install peers
|
||||
auto-install-peers=true
|
||||
|
||||
# Shamefully hoist - necessary for some packages
|
||||
# Shamefully hoist - neccessary for some packages
|
||||
shamefully-hoist=true
|
||||
|
||||
# Node linker - use hoisted for full compatibility
|
||||
@ -20,3 +12,6 @@ node-linker=hoisted
|
||||
|
||||
# Lockfile settings
|
||||
lockfile=true
|
||||
|
||||
# Optional: Store location (uncomment if you want to customize)
|
||||
# store-dir=.pnpm-store
|
||||
|
||||
@ -1,65 +0,0 @@
|
||||
# CLOUDFLARE TUNNEL - SIN PORT FORWARDING
|
||||
|
||||
## ☁️ Cloudflare Tunnel para LiveKit (Avanzado)
|
||||
|
||||
### Ventajas:
|
||||
- ✅ **Sin port forwarding** en router
|
||||
- ✅ **SSL automático**
|
||||
- ✅ **Protección DDoS**
|
||||
- ✅ **IP oculta**
|
||||
|
||||
### ⚠️ Limitaciones para WebRTC:
|
||||
- ❌ **UDP no soportado** directamente
|
||||
- ⚠️ **Requiere TURN server** para WebRTC
|
||||
- 🔧 **Solo TCP/HTTP** a través del tunnel
|
||||
|
||||
### Configuración (solo si tienes TURN server):
|
||||
|
||||
#### Paso 1: Instalar cloudflared
|
||||
```bash
|
||||
# Descargar cloudflared
|
||||
curl -L --output cloudflared.deb https://github.com/cloudflare/cloudflared/releases/latest/download/cloudflared-linux-amd64.deb
|
||||
sudo dpkg -i cloudflared.deb
|
||||
```
|
||||
|
||||
#### Paso 2: Crear tunnel
|
||||
```bash
|
||||
# Login a Cloudflare
|
||||
cloudflared tunnel login
|
||||
|
||||
# Crear tunnel
|
||||
cloudflared tunnel create livekit-tunnel
|
||||
|
||||
# Configurar tunnel
|
||||
cat > ~/.cloudflared/config.yml << 'EOF'
|
||||
tunnel: livekit-tunnel
|
||||
credentials-file: /home/usuario/.cloudflared/livekit-tunnel.json
|
||||
|
||||
ingress:
|
||||
- hostname: livekit.midominio.com
|
||||
service: http://localhost:7880
|
||||
- service: http_status:404
|
||||
EOF
|
||||
|
||||
# Crear DNS record
|
||||
cloudflared tunnel route dns livekit-tunnel livekit.midominio.com
|
||||
|
||||
# Ejecutar tunnel
|
||||
cloudflared tunnel run livekit-tunnel
|
||||
```
|
||||
|
||||
#### Configuración LiveKit (necesita TURN):
|
||||
```yaml
|
||||
# livekit-production.yaml
|
||||
rtc:
|
||||
# SIN puertos UDP directos - usar TURN
|
||||
use_external_ip: false
|
||||
|
||||
ice_servers:
|
||||
- urls: ["stun:stun.l.google.com:19302"]
|
||||
- urls: ["turn:turn.midominio.com:3478"]
|
||||
username: "usuario"
|
||||
credential: "password"
|
||||
```
|
||||
|
||||
### ⚠️ **NO RECOMENDADO** para LiveKit porque WebRTC necesita UDP
|
||||
@ -1,65 +0,0 @@
|
||||
# CONFIGURACIÓN AVANZADA: ACCESO DESDE INTERNET
|
||||
|
||||
## 🌍 Para usuarios externos (más complejo y riesgoso)
|
||||
|
||||
### ⚠️ PROBLEMAS de exponer UDP públicamente:
|
||||
|
||||
1. **Seguridad**: 10,000 puertos UDP expuestos
|
||||
2. **Complejidad**: Port forwarding masivo
|
||||
3. **NAT Traversal**: Problemas con diferentes ISPs
|
||||
4. **Mantenimiento**: Configuración de router compleja
|
||||
|
||||
### Si NECESITAS acceso externo, opciones:
|
||||
|
||||
#### Opción A: TURN Server (RECOMENDADO)
|
||||
```yaml
|
||||
# livekit-public.yaml
|
||||
rtc:
|
||||
use_external_ip: true
|
||||
external_ip: "TU_IP_PUBLICA"
|
||||
|
||||
# Usar TURN para atravesar NAT
|
||||
ice_servers:
|
||||
- urls: ["stun:stun.l.google.com:19302"]
|
||||
- urls: ["turn:tu-turn-server.com:3478"]
|
||||
username: "usuario"
|
||||
credential: "password"
|
||||
|
||||
# Rango reducido de puertos
|
||||
port_range_start: 50000
|
||||
port_range_end: 50100 # Solo 100 puertos
|
||||
```
|
||||
|
||||
#### Opción B: LiveKit Cloud (MÁS RECOMENDADO)
|
||||
```env
|
||||
# .env.production
|
||||
LIVEKIT_URL=wss://tu-proyecto.livekit.cloud
|
||||
LIVEKIT_API_KEY=cloud-api-key
|
||||
LIVEKIT_API_SECRET=cloud-secret
|
||||
|
||||
# ✅ SIN configuración UDP local
|
||||
# ✅ Sin port forwarding
|
||||
# ✅ Infraestructura optimizada
|
||||
```
|
||||
|
||||
#### Opción C: VPN (Para usuarios conocidos)
|
||||
```bash
|
||||
# Configurar WireGuard/OpenVPN
|
||||
# Usuarios se conectan por VPN a tu red local
|
||||
# Acceso como si fueran locales
|
||||
|
||||
# No requiere exponer UDP públicamente
|
||||
# Acceso seguro y controlado
|
||||
```
|
||||
|
||||
### Port Forwarding (Solo si es absolutamente necesario):
|
||||
```
|
||||
⚠️ EN ROUTER:
|
||||
UDP 50000-60000 → 192.168.1.19:50000-60000
|
||||
|
||||
❌ RIESGOS:
|
||||
- 10,000 puertos UDP expuestos
|
||||
- Posibles ataques DDoS
|
||||
- Configuración compleja
|
||||
- Problemas con CGN/CGNAT de ISPs
|
||||
```
|
||||
@ -1,36 +0,0 @@
|
||||
# CONFIGURACIÓN RECOMENDADA: SOLO RED LOCAL
|
||||
|
||||
## 🔒 Para uso en red local ÚNICAMENTE
|
||||
|
||||
### Firewall UFW (SOLO red local):
|
||||
```bash
|
||||
# Permitir desde red local solamente
|
||||
sudo ufw allow from 192.168.1.0/24 to any port 50000:60000 proto udp
|
||||
sudo ufw allow from 192.168.1.0/24 to any port 7880 proto tcp
|
||||
sudo ufw allow from 192.168.1.0/24 to any port 80 proto tcp
|
||||
|
||||
# BLOQUEAR acceso externo a UDP
|
||||
sudo ufw deny 50000:60000/udp
|
||||
|
||||
# Verificar que solo red local tenga acceso
|
||||
sudo ufw status numbered
|
||||
```
|
||||
|
||||
### Router/Modem (NO configurar port forwarding):
|
||||
```
|
||||
❌ NO hacer port forwarding de UDP 50000-60000
|
||||
✅ Solo HTTP (puerto 80) si necesitas acceso web externo
|
||||
✅ Usar VPN si necesitas acceso remoto
|
||||
```
|
||||
|
||||
### Ventajas:
|
||||
- ✅ **Seguridad**: UDP no expuesto a internet
|
||||
- ✅ **Simplicidad**: Sin configuración de router
|
||||
- ✅ **Rendimiento**: Conexión directa en LAN
|
||||
- ✅ **Sin NAT**: Sin problemas de traversal
|
||||
|
||||
### Casos de uso:
|
||||
- Oficina local
|
||||
- Casa/familia
|
||||
- Reuniones internas
|
||||
- Desarrollo y testing
|
||||
@ -1,107 +0,0 @@
|
||||
# CONFIGURACIÓN DOMINIO PROPIO PARA LIVEKIT
|
||||
|
||||
## 🏠 Dominio propio (ej: livekit.midominio.com)
|
||||
|
||||
### Opción A: Subdominio de tu dominio existente
|
||||
|
||||
#### Paso 1: Configurar DNS
|
||||
```
|
||||
Tipo: A
|
||||
Nombre: livekit
|
||||
Valor: TU_IP_PUBLICA
|
||||
TTL: 300
|
||||
|
||||
Resultado: livekit.midominio.com → TU_IP_PUBLICA
|
||||
```
|
||||
|
||||
#### Paso 2: Port forwarding en router
|
||||
```
|
||||
Puerto 80 → 192.168.1.19:80 # HTTP para Let's Encrypt
|
||||
Puerto 443 → 192.168.1.19:443 # HTTPS/WSS
|
||||
Puerto 7880 → 192.168.1.19:7880 # LiveKit API directo
|
||||
Puerto 50000-50100 (UDP) → 192.168.1.19:50000-50100 # WebRTC
|
||||
```
|
||||
|
||||
#### Paso 3: SSL con Let's Encrypt
|
||||
```bash
|
||||
# Instalar certbot
|
||||
sudo apt update
|
||||
sudo apt install certbot nginx
|
||||
|
||||
# Configurar Nginx básico
|
||||
sudo tee /etc/nginx/sites-available/livekit << 'EOF'
|
||||
server {
|
||||
listen 80;
|
||||
server_name livekit.midominio.com;
|
||||
|
||||
location /.well-known/acme-challenge/ {
|
||||
root /var/www/html;
|
||||
}
|
||||
|
||||
location / {
|
||||
return 301 https://$server_name$request_uri;
|
||||
}
|
||||
}
|
||||
EOF
|
||||
|
||||
sudo ln -s /etc/nginx/sites-available/livekit /etc/nginx/sites-enabled/
|
||||
sudo nginx -t && sudo systemctl restart nginx
|
||||
|
||||
# Generar certificado SSL
|
||||
sudo certbot --nginx -d livekit.midominio.com
|
||||
|
||||
# Resultado: certificados en /etc/letsencrypt/live/livekit.midominio.com/
|
||||
```
|
||||
|
||||
#### Paso 4: Configurar Nginx para LiveKit
|
||||
```nginx
|
||||
# /etc/nginx/sites-available/livekit
|
||||
server {
|
||||
listen 443 ssl http2;
|
||||
server_name livekit.midominio.com;
|
||||
|
||||
ssl_certificate /etc/letsencrypt/live/livekit.midominio.com/fullchain.pem;
|
||||
ssl_certificate_key /etc/letsencrypt/live/livekit.midominio.com/privkey.pem;
|
||||
|
||||
# WebSocket proxy para LiveKit
|
||||
location / {
|
||||
proxy_pass http://localhost:7880;
|
||||
proxy_http_version 1.1;
|
||||
proxy_set_header Upgrade $http_upgrade;
|
||||
proxy_set_header Connection "upgrade";
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
proxy_set_header X-Forwarded-Proto $scheme;
|
||||
|
||||
# Timeouts para WebRTC
|
||||
proxy_connect_timeout 60s;
|
||||
proxy_send_timeout 60s;
|
||||
proxy_read_timeout 60s;
|
||||
}
|
||||
}
|
||||
|
||||
server {
|
||||
listen 80;
|
||||
server_name livekit.midominio.com;
|
||||
return 301 https://$server_name$request_uri;
|
||||
}
|
||||
```
|
||||
|
||||
#### Paso 5: Auto-renovación SSL
|
||||
```bash
|
||||
# Agregar a crontab
|
||||
sudo crontab -e
|
||||
|
||||
# Renovar certificados automáticamente
|
||||
0 12 * * * /usr/bin/certbot renew --quiet && systemctl reload nginx
|
||||
```
|
||||
|
||||
### URLs finales:
|
||||
- **LiveKit WSS**: `wss://livekit.midominio.com`
|
||||
- **API HTTPS**: `https://livekit.midominio.com`
|
||||
|
||||
### Configurar en OpenVidu Meet:
|
||||
```env
|
||||
LIVEKIT_URL=wss://livekit.midominio.com
|
||||
```
|
||||
@ -1,72 +0,0 @@
|
||||
# CONFIGURACIÓN DUCKDNS PARA LIVEKIT
|
||||
|
||||
## 🦆 DuckDNS - Dominio gratuito para IP dinámica
|
||||
|
||||
### Paso 1: Registrarse en DuckDNS
|
||||
1. Ir a [duckdns.org](https://www.duckdns.org)
|
||||
2. Login con Google/GitHub
|
||||
3. Crear subdominio: `mi-livekit.duckdns.org`
|
||||
4. Copiar el token
|
||||
|
||||
### Paso 2: Script de actualización automática
|
||||
```bash
|
||||
# Crear script de actualización
|
||||
cat > /home/xesar/update-duckdns.sh << 'EOF'
|
||||
#!/bin/bash
|
||||
# Actualizar DuckDNS con IP actual
|
||||
|
||||
DOMAIN="mi-livekit" # Tu subdominio sin .duckdns.org
|
||||
TOKEN="tu-token-aqui" # Token de DuckDNS
|
||||
|
||||
# Obtener IP pública actual
|
||||
CURRENT_IP=$(curl -s https://checkip.amazonaws.com)
|
||||
|
||||
# Actualizar DuckDNS
|
||||
RESPONSE=$(curl -s "https://www.duckdns.org/update?domains=$DOMAIN&token=$TOKEN&ip=$CURRENT_IP")
|
||||
|
||||
if [ "$RESPONSE" = "OK" ]; then
|
||||
echo "$(date): DuckDNS actualizado - $DOMAIN.duckdns.org → $CURRENT_IP"
|
||||
else
|
||||
echo "$(date): ERROR actualizando DuckDNS: $RESPONSE"
|
||||
fi
|
||||
EOF
|
||||
|
||||
chmod +x /home/xesar/update-duckdns.sh
|
||||
```
|
||||
|
||||
### Paso 3: Automatizar con cron
|
||||
```bash
|
||||
# Editar crontab
|
||||
crontab -e
|
||||
|
||||
# Agregar línea para actualizar cada 5 minutos:
|
||||
*/5 * * * * /home/xesar/update-duckdns.sh >> /home/xesar/duckdns.log 2>&1
|
||||
```
|
||||
|
||||
### Paso 4: Configurar LiveKit con dominio
|
||||
```yaml
|
||||
# livekit-production.yaml
|
||||
rtc:
|
||||
external_ip: "mi-livekit.duckdns.org" # Tu dominio DuckDNS
|
||||
port_range_start: 50000
|
||||
port_range_end: 50100
|
||||
```
|
||||
|
||||
### Paso 5: Port forwarding en router
|
||||
```
|
||||
Regla: LiveKit-API
|
||||
- Puerto externo: 7880
|
||||
- Puerto interno: 7880
|
||||
- IP: 192.168.1.19
|
||||
- Protocolo: TCP
|
||||
|
||||
Regla: LiveKit-WebRTC
|
||||
- Puerto externo: 50000-50100
|
||||
- Puerto interno: 50000-50100
|
||||
- IP: 192.168.1.19
|
||||
- Protocolo: UDP
|
||||
```
|
||||
|
||||
### URLs finales:
|
||||
- **LiveKit API**: `ws://mi-livekit.duckdns.org:7880`
|
||||
- **Con SSL**: `wss://mi-livekit.duckdns.org:443` (después de configurar SSL)
|
||||
67
Dockerfile
67
Dockerfile
@ -1,67 +0,0 @@
|
||||
# Multi-stage build para OpenVidu Meet
|
||||
FROM node:20-alpine AS builder
|
||||
|
||||
# Instalar pnpm
|
||||
RUN npm install -g pnpm
|
||||
|
||||
# Copiar archivos del workspace
|
||||
WORKDIR /app
|
||||
COPY package.json pnpm-workspace.yaml pnpm-lock.yaml ./
|
||||
COPY meet-ce/backend/package.json ./meet-ce/backend/
|
||||
COPY meet-ce/frontend/package.json ./meet-ce/frontend/
|
||||
COPY meet-ce/frontend/projects/shared-meet-components/package.json ./meet-ce/frontend/projects/shared-meet-components/
|
||||
|
||||
# Instalar dependencias
|
||||
RUN pnpm install --frozen-lockfile
|
||||
|
||||
# Copiar código fuente
|
||||
COPY . .
|
||||
|
||||
# Build backend
|
||||
WORKDIR /app/meet-ce/backend
|
||||
RUN pnpm run build
|
||||
|
||||
# Build frontend
|
||||
WORKDIR /app/meet-ce/frontend
|
||||
RUN pnpm run build:prod
|
||||
|
||||
# Imagen de producción
|
||||
FROM node:20-alpine AS production
|
||||
|
||||
# Instalar pnpm
|
||||
RUN npm install -g pnpm
|
||||
|
||||
# Crear usuario no-root
|
||||
RUN addgroup -g 1001 -S nodejs && \
|
||||
adduser -S openvidu -u 1001
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
# Copiar package.json y dependencias
|
||||
COPY package.json pnpm-workspace.yaml pnpm-lock.yaml ./
|
||||
COPY meet-ce/backend/package.json ./meet-ce/backend/
|
||||
|
||||
# Instalar solo dependencias de producción
|
||||
RUN pnpm install --prod --frozen-lockfile
|
||||
|
||||
# Copiar archivos compilados
|
||||
COPY --from=builder /app/meet-ce/backend/dist ./meet-ce/backend/dist
|
||||
COPY --from=builder /app/meet-ce/backend/public ./meet-ce/backend/public
|
||||
|
||||
# Cambiar propietario
|
||||
RUN chown -R openvidu:nodejs /app
|
||||
|
||||
USER openvidu
|
||||
|
||||
# Variables de entorno por defecto
|
||||
ENV NODE_ENV=production
|
||||
ENV PORT=6080
|
||||
ENV MEET_BLOB_STORAGE_MODE=memory
|
||||
ENV MEET_LOG_LEVEL=info
|
||||
|
||||
# Exponer puerto
|
||||
EXPOSE 6080
|
||||
|
||||
# Comando de inicio
|
||||
WORKDIR /app/meet-ce/backend
|
||||
CMD ["node", "dist/src/server.js"]
|
||||
@ -1,52 +0,0 @@
|
||||
# COMPARACIÓN: OPCIONES PARA EASYPANEL
|
||||
|
||||
## 📊 Tabla comparativa
|
||||
|
||||
| Opción | Configuración UDP | Costo | Complejidad | Rendimiento | Recomendado |
|
||||
|--------|------------------|-------|-------------|-------------|-------------|
|
||||
| **LiveKit Cloud** | ❌ No necesario | $20-100/mes | ⭐ Muy fácil | ⭐⭐⭐ Excelente | ✅ **SÍ** |
|
||||
| **VPS Híbrido** | ✅ En VPS separado | $10-30/mes | ⭐⭐ Medio | ⭐⭐⭐ Excelente | ⚠️ Si budget limitado |
|
||||
| **TURN Server** | ✅ 3 puertos en VPS | $5-15/mes | ⭐⭐⭐ Complejo | ⭐⭐ Bueno | ⚠️ Para expertos |
|
||||
| **EasyPanel Solo** | ❌ Imposible | $0 extra | ❌ No funciona | ❌ No WebRTC | ❌ NO |
|
||||
|
||||
## 🏆 RECOMENDACIÓN FINAL
|
||||
|
||||
### Para la mayoría de casos: **LiveKit Cloud**
|
||||
|
||||
```env
|
||||
# .env.production para EasyPanel
|
||||
LIVEKIT_URL=wss://tu-proyecto.livekit.cloud
|
||||
LIVEKIT_API_KEY=api-key-de-cloud
|
||||
LIVEKIT_API_SECRET=secret-de-cloud
|
||||
```
|
||||
|
||||
### Ventajas de LiveKit Cloud:
|
||||
- ✅ **Cero configuración UDP**
|
||||
- ✅ **Sin VPS adicionales**
|
||||
- ✅ **Infraestructura global**
|
||||
- ✅ **Escalabilidad automática**
|
||||
- ✅ **Soporte oficial**
|
||||
- ✅ **TURN servers incluidos**
|
||||
- ✅ **Monitoreo y analytics**
|
||||
|
||||
### Costos LiveKit Cloud:
|
||||
- **Free tier**: 50GB/mes gratis
|
||||
- **Starter**: $20/mes - 500GB
|
||||
- **Pro**: $99/mes - 2TB + features
|
||||
|
||||
### Setup LiveKit Cloud:
|
||||
1. **Registro**: https://cloud.livekit.io
|
||||
2. **Crear proyecto**
|
||||
3. **Copiar credenciales**
|
||||
4. **Configurar en EasyPanel**
|
||||
5. **Deploy** ✅
|
||||
|
||||
## 🎯 PASOS SIGUIENTE PARA TI
|
||||
|
||||
¿Qué opción prefieres?
|
||||
|
||||
1. **LiveKit Cloud** (fácil, costo medio)
|
||||
2. **VPS Híbrido** (control total, setup complejo)
|
||||
3. **TURN Server** (experto, costo bajo)
|
||||
|
||||
Te ayudo a configurar la que elijas.
|
||||
@ -1,113 +0,0 @@
|
||||
# CONFIGURACIÓN EASYPANEL - OpenVidu Meet
|
||||
# ========================================
|
||||
|
||||
## 📋 CONFIGURACIÓN DEL PROYECTO
|
||||
|
||||
### 1. Crear Proyecto en EasyPanel
|
||||
- Tipo: Docker Compose o Docker Build
|
||||
- Repositorio: Tu repo con estos archivos
|
||||
- Branch: main
|
||||
|
||||
### 2. Variables de Entorno Requeridas
|
||||
```env
|
||||
# Básicas
|
||||
NODE_ENV=production
|
||||
MEET_LOG_LEVEL=info
|
||||
MEET_BLOB_STORAGE_MODE=memory
|
||||
|
||||
# Admin (CAMBIAR)
|
||||
ADMIN_PASSWORD=tu-password-seguro-aqui
|
||||
|
||||
# LiveKit (ajustar según tu setup)
|
||||
LIVEKIT_URL=wss://tu-livekit-domain.com
|
||||
LIVEKIT_API_KEY=tu-api-key
|
||||
LIVEKIT_API_SECRET=tu-secret-32-caracteres-minimo
|
||||
|
||||
# Proxy
|
||||
TRUST_PROXY=true
|
||||
SERVER_CORS_ORIGIN=*
|
||||
USE_HTTPS=true
|
||||
```
|
||||
|
||||
### 3. Configuración de Puertos
|
||||
- **Opción A (con Nginx)**: Puerto 80
|
||||
- **Opción B (directo)**: Puerto 6080
|
||||
|
||||
### 4. Configuración de Dominio
|
||||
- Agregar tu dominio en EasyPanel
|
||||
- Habilitar SSL automático
|
||||
- Configurar redirects HTTP → HTTPS
|
||||
|
||||
## 🐳 OPCIONES DE DEPLOY
|
||||
|
||||
### Opción A: Con Nginx Proxy (Recomendado)
|
||||
```yaml
|
||||
# Usar docker-compose.yml completo
|
||||
# Puerto expuesto: 80/443
|
||||
# Incluye rate limiting y optimizaciones
|
||||
```
|
||||
|
||||
### Opción B: Solo Backend
|
||||
```yaml
|
||||
# Solo el servicio openvidu-meet del compose
|
||||
# Puerto expuesto: 6080
|
||||
# EasyPanel maneja el proxy
|
||||
```
|
||||
|
||||
## 🔧 CONFIGURACIONES ADICIONALES
|
||||
|
||||
### LiveKit Setup
|
||||
1. Desplegar LiveKit en servidor separado
|
||||
2. Configurar LIVEKIT_URL con dominio público
|
||||
3. Generar API keys seguros
|
||||
|
||||
### Redis (Opcional)
|
||||
- Usar servicio Redis de EasyPanel
|
||||
- O mantener storage en memoria para simplicidad
|
||||
|
||||
### SSL/TLS
|
||||
- EasyPanel maneja certificados automáticamente
|
||||
- Configurar HTTPS en variables de entorno
|
||||
|
||||
## 🚨 CONSIDERACIONES DE SEGURIDAD
|
||||
|
||||
1. **Cambiar credenciales por defecto**
|
||||
2. **Usar secrets seguros para LiveKit**
|
||||
3. **Configurar CORS apropiadamente**
|
||||
4. **Habilitar rate limiting**
|
||||
5. **Usar HTTPS únicamente**
|
||||
|
||||
## 📊 MONITOREO
|
||||
|
||||
### Health Checks
|
||||
- `/nginx-health` - Estado del proxy
|
||||
- `/api/health` - Estado del backend
|
||||
- Logs en EasyPanel dashboard
|
||||
|
||||
### Métricas
|
||||
- CPU/Memory usage
|
||||
- Response times
|
||||
- Error rates
|
||||
|
||||
## 🔄 ACTUALIZACIONES
|
||||
|
||||
1. Push cambios al repo
|
||||
2. EasyPanel rebuilds automáticamente
|
||||
3. Zero-downtime deployment
|
||||
|
||||
## 🐛 TROUBLESHOOTING
|
||||
|
||||
### Backend no arranca
|
||||
- Verificar variables de entorno
|
||||
- Revisar logs en EasyPanel
|
||||
- Verificar puertos
|
||||
|
||||
### Error de proxy
|
||||
- Verificar nginx.conf
|
||||
- Revisar headers
|
||||
- Verificar upstreams
|
||||
|
||||
### LiveKit no conecta
|
||||
- Verificar LIVEKIT_URL
|
||||
- Verificar API keys
|
||||
- Verificar connectivity
|
||||
@ -1,164 +0,0 @@
|
||||
# 🚀 **DEPLOYMENT EN EASYPANEL - SIMPLIFICADO**
|
||||
|
||||
## 📋 **RESUMEN DE CONFIGURACIÓN**
|
||||
|
||||
✅ **EasyPanel maneja automáticamente:**
|
||||
- SSL/TLS con certificados gratuitos
|
||||
- Subdominios (ej: `tu-app.easypanel.host`)
|
||||
- Proxy reverso con Traefik
|
||||
- HTTPS redirect
|
||||
|
||||
✅ **Tu aplicación expone:**
|
||||
- Solo puerto 80 (HTTP)
|
||||
- Nginx como proxy interno
|
||||
- OpenVidu Meet backend
|
||||
|
||||
---
|
||||
|
||||
## 🔧 **PASOS DE DEPLOYMENT**
|
||||
|
||||
### **1. Preparar archivos**
|
||||
```bash
|
||||
# Los archivos ya están listos:
|
||||
# ✅ Dockerfile (optimizado)
|
||||
# ✅ docker-compose.yml (puerto 80 solamente)
|
||||
# ✅ nginx.conf (sin SSL - solo HTTP)
|
||||
# ✅ .env.production (variables de entorno)
|
||||
```
|
||||
|
||||
### **2. Subir a repositorio Git**
|
||||
```bash
|
||||
git add Dockerfile docker-compose.yml nginx.conf .env.production
|
||||
git commit -m "Add EasyPanel deployment config"
|
||||
git push
|
||||
```
|
||||
|
||||
### **3. Crear proyecto en EasyPanel**
|
||||
|
||||
#### **Opción A: Docker Compose (Recomendado)**
|
||||
1. **Nuevo Proyecto** → **Deploy from Git**
|
||||
2. **Conectar repositorio**
|
||||
3. **Tipo:** Docker Compose
|
||||
4. **Archivo:** `docker-compose.yml`
|
||||
5. **Puerto expuesto:** `80`
|
||||
|
||||
#### **Opción B: Dockerfile simple**
|
||||
1. **Nuevo Proyecto** → **Deploy from Git**
|
||||
2. **Tipo:** Dockerfile
|
||||
3. **Puerto:** `6080`
|
||||
4. **Health Check:** `/health`
|
||||
|
||||
### **4. Variables de entorno en EasyPanel**
|
||||
|
||||
En el dashboard de EasyPanel, configurar:
|
||||
|
||||
```env
|
||||
# ADMIN (¡CAMBIAR!)
|
||||
ADMIN_PASSWORD=mi-password-super-seguro
|
||||
|
||||
# LIVEKIT (configurar según tu setup)
|
||||
LIVEKIT_URL=wss://tu-livekit-domain.com
|
||||
LIVEKIT_API_KEY=tu-api-key
|
||||
LIVEKIT_API_SECRET=tu-secret-de-32-caracteres
|
||||
|
||||
# REDIS (opcional)
|
||||
REDIS_HOST=tu-redis-host
|
||||
REDIS_PASSWORD=tu-redis-password
|
||||
```
|
||||
|
||||
### **5. Deploy**
|
||||
- Hacer clic en **Deploy**
|
||||
- EasyPanel construirá automáticamente
|
||||
- Generará subdominio (ej: `openvidu-meet.easypanel.host`)
|
||||
- Aplicará SSL automáticamente
|
||||
|
||||
---
|
||||
|
||||
## 🌐 **RESULTADO FINAL**
|
||||
|
||||
```
|
||||
https://tu-app.easypanel.host
|
||||
├── EasyPanel Traefik Proxy (SSL/HTTPS)
|
||||
└── Tu Container (puerto 80)
|
||||
├── Nginx (proxy interno)
|
||||
└── OpenVidu Meet Backend (:6080)
|
||||
```
|
||||
|
||||
### **URLs disponibles:**
|
||||
- **Aplicación:** `https://tu-app.easypanel.host`
|
||||
- **Admin Login:** `https://tu-app.easypanel.host/admin`
|
||||
- **API:** `https://tu-app.easypanel.host/api/`
|
||||
- **Health Check:** `https://tu-app.easypanel.host/health`
|
||||
|
||||
---
|
||||
|
||||
## 🔧 **CONFIGURACIÓN LIVEKIT**
|
||||
|
||||
Para que funcione completamente, necesitas **LiveKit server** separado:
|
||||
|
||||
### **Opción 1: LiveKit en EasyPanel (otro proyecto)**
|
||||
```yaml
|
||||
# livekit.yaml para EasyPanel
|
||||
port: 7880
|
||||
redis:
|
||||
address: tu-redis:6379
|
||||
password: tu-password
|
||||
```
|
||||
|
||||
### **Opción 2: LiveKit Cloud**
|
||||
- Registrarse en [LiveKit Cloud](https://cloud.livekit.io)
|
||||
- Copiar `LIVEKIT_URL`, `API_KEY`, `API_SECRET`
|
||||
- Configurar en variables de entorno
|
||||
|
||||
---
|
||||
|
||||
## 🔐 **SEGURIDAD**
|
||||
|
||||
### **Cambiar credenciales por defecto:**
|
||||
```env
|
||||
ADMIN_PASSWORD=un-password-muy-seguro
|
||||
LIVEKIT_API_SECRET=secret-de-al-menos-32-caracteres
|
||||
```
|
||||
|
||||
### **Headers de seguridad incluidos:**
|
||||
- Rate limiting (API: 10req/s, Login: 1req/s)
|
||||
- X-Frame-Options
|
||||
- X-Content-Type-Options
|
||||
- X-XSS-Protection
|
||||
|
||||
---
|
||||
|
||||
## 🚨 **TROUBLESHOOTING**
|
||||
|
||||
### **El container no inicia:**
|
||||
```bash
|
||||
# Ver logs en EasyPanel dashboard
|
||||
# O conectar por SSH:
|
||||
docker logs container-name
|
||||
```
|
||||
|
||||
### **502 Bad Gateway:**
|
||||
- Verificar que el backend responde en puerto 6080
|
||||
- Health check: `curl localhost:6080/health`
|
||||
|
||||
### **WebSocket no funciona:**
|
||||
- Verificar configuración de LiveKit
|
||||
- Headers de WebSocket están configurados en nginx
|
||||
|
||||
### **Admin login no funciona:**
|
||||
- Verificar variable `ADMIN_PASSWORD`
|
||||
- Limpiar datos Redis si está configurado
|
||||
|
||||
---
|
||||
|
||||
## ✅ **CHECKLIST FINAL**
|
||||
|
||||
- [ ] Repository con archivos de deployment subido
|
||||
- [ ] Proyecto creado en EasyPanel
|
||||
- [ ] Variables de entorno configuradas
|
||||
- [ ] Password admin cambiado
|
||||
- [ ] LiveKit configurado (separado)
|
||||
- [ ] SSL funcionando automáticamente
|
||||
- [ ] Admin login funcional en `/admin`
|
||||
|
||||
**¡Ya tienes OpenVidu Meet funcionando en producción con EasyPanel!** 🎉
|
||||
@ -1,104 +0,0 @@
|
||||
# SOLUCIÓN: TURN SERVER PARA EASYPANEL
|
||||
|
||||
## 🔄 TURN Server como alternativa para NAT traversal
|
||||
|
||||
### ¿Qué es TURN?
|
||||
TURN (Traversal Using Relays around NAT) permite que WebRTC funcione sin exponer miles de puertos UDP.
|
||||
|
||||
## 🏗️ ARQUITECTURA CON TURN
|
||||
|
||||
```
|
||||
Cliente → Internet → TURN Server → EasyPanel
|
||||
(3 puertos) (sin UDP)
|
||||
|
||||
Vs. directo:
|
||||
Cliente → Internet → EasyPanel
|
||||
(10,000 UDP) ❌ No posible
|
||||
```
|
||||
|
||||
## 📋 IMPLEMENTACIÓN
|
||||
|
||||
### 1. TURN Server en VPS separado
|
||||
```bash
|
||||
# Instalar Coturn en VPS
|
||||
apt-get update
|
||||
apt-get install coturn
|
||||
|
||||
# /etc/turnserver.conf
|
||||
listening-port=3478
|
||||
tls-listening-port=5349
|
||||
external-ip=IP_PUBLICA_VPS
|
||||
realm=turn.tu-dominio.com
|
||||
lt-cred-mech
|
||||
user=usuario:password123
|
||||
verbose
|
||||
```
|
||||
|
||||
### 2. Firewall VPS (Solo 3 puertos)
|
||||
```bash
|
||||
# Solo estos 3 puertos para TURN
|
||||
ufw allow 3478/tcp # TURN TCP
|
||||
ufw allow 3478/udp # TURN UDP
|
||||
ufw allow 5349/tcp # TURN over TLS
|
||||
ufw enable
|
||||
```
|
||||
|
||||
### 3. LiveKit en EasyPanel con TURN
|
||||
```yaml
|
||||
# livekit.yaml para EasyPanel
|
||||
port: 7880
|
||||
|
||||
keys:
|
||||
devkey: tu-secret-32-chars
|
||||
|
||||
# SIN puertos UDP locales - usar TURN
|
||||
rtc:
|
||||
# NO port_range - usa TURN
|
||||
use_external_ip: false
|
||||
|
||||
# Configurar TURN servers
|
||||
ice_servers:
|
||||
- urls: ["stun:stun.l.google.com:19302"]
|
||||
- urls: ["turn:turn.tu-dominio.com:3478"]
|
||||
username: "usuario"
|
||||
credential: "password123"
|
||||
- urls: ["turns:turn.tu-dominio.com:5349"]
|
||||
username: "usuario"
|
||||
credential: "password123"
|
||||
```
|
||||
|
||||
### 4. Variables EasyPanel
|
||||
```env
|
||||
# Solo TCP - SIN UDP
|
||||
LIVEKIT_URL=wss://tu-app.easypanel.host/livekit
|
||||
LIVEKIT_API_KEY=devkey
|
||||
LIVEKIT_API_SECRET=tu-secret-32-chars
|
||||
```
|
||||
|
||||
### 5. Nginx en EasyPanel para proxy LiveKit
|
||||
```nginx
|
||||
# nginx.conf - agregar ruta para LiveKit
|
||||
location /livekit {
|
||||
proxy_pass http://openvidu-meet:7880;
|
||||
proxy_http_version 1.1;
|
||||
proxy_set_header Upgrade $http_upgrade;
|
||||
proxy_set_header Connection "upgrade";
|
||||
}
|
||||
```
|
||||
|
||||
## 💰 COSTOS
|
||||
- **TURN VPS**: $5-10/mes (pequeño VPS)
|
||||
- **EasyPanel**: Tu plan actual
|
||||
- **Total**: +$5-10/mes vs LiveKit Cloud
|
||||
|
||||
## ✅ VENTAJAS
|
||||
- ✅ Solo 3 puertos UDP en VPS externo
|
||||
- ✅ EasyPanel sin UDP
|
||||
- ✅ NAT traversal garantizado
|
||||
- ✅ Menor costo que LiveKit Cloud
|
||||
|
||||
## ❌ DESVENTAJAS
|
||||
- ❌ Configuración más compleja
|
||||
- ❌ VPS adicional para TURN
|
||||
- ❌ Latencia adicional (relay)
|
||||
- ❌ Ancho de banda TURN server
|
||||
@ -1,29 +0,0 @@
|
||||
# LIMITACIONES DE EASYPANEL PARA UDP
|
||||
|
||||
## ❌ Por qué EasyPanel NO puede exponer UDP:
|
||||
|
||||
### Arquitectura de EasyPanel:
|
||||
```
|
||||
Internet → Traefik (HTTP/HTTPS Proxy) → Tu Container
|
||||
↑
|
||||
Solo maneja TCP/HTTP/HTTPS
|
||||
NO puede proxy UDP
|
||||
```
|
||||
|
||||
### Limitaciones técnicas:
|
||||
1. **Traefik**: Solo HTTP/HTTPS reverse proxy
|
||||
2. **Docker networking**: Limitado a puertos TCP expuestos
|
||||
3. **UI de EasyPanel**: Solo configuración HTTP
|
||||
4. **Load balancing**: Diseñado para web apps, no media streaming
|
||||
|
||||
### Puertos disponibles en EasyPanel:
|
||||
- ✅ 80 (HTTP)
|
||||
- ✅ 443 (HTTPS)
|
||||
- ✅ Puertos TCP custom
|
||||
- ❌ Puertos UDP (NO DISPONIBLE)
|
||||
|
||||
## ⚠️ Problemas si intentas exponer UDP:
|
||||
- EasyPanel UI no tiene opción para UDP
|
||||
- Traefik no puede hacer proxy de UDP
|
||||
- Docker compose limitado a TCP en EasyPanel
|
||||
- No hay configuración de port ranges UDP
|
||||
@ -1,129 +0,0 @@
|
||||
# SOLUCIÓN: LIVEKIT EN VPS SEPARADO + EASYPANEL
|
||||
|
||||
## 🏗️ ARQUITECTURA HÍBRIDA
|
||||
|
||||
```
|
||||
┌─ EasyPanel ────────────────┐ ┌─ VPS Separado ─────────┐
|
||||
│ │ │ │
|
||||
│ OpenVidu Meet Backend ──────────→ LiveKit Server │
|
||||
│ (HTTP/HTTPS only) │ │ (UDP 50000-60000) │
|
||||
│ │ │ │
|
||||
└────────────────────────────┘ └────────────────────────┘
|
||||
↑ ↑
|
||||
Traefik/SSL Firewall/UDP abierto
|
||||
```
|
||||
|
||||
## 📋 CONFIGURACIÓN PASO A PASO
|
||||
|
||||
### 1. EasyPanel (Solo OpenVidu Meet Backend)
|
||||
```yaml
|
||||
# docker-compose.yml para EasyPanel
|
||||
version: '3.8'
|
||||
services:
|
||||
openvidu-meet:
|
||||
build: .
|
||||
environment:
|
||||
# LiveKit en VPS externo
|
||||
LIVEKIT_URL: wss://livekit.tu-vps.com:7880
|
||||
LIVEKIT_API_KEY: devkey
|
||||
LIVEKIT_API_SECRET: tu-secret-32-chars
|
||||
ports:
|
||||
- "80:6080" # Solo HTTP - EasyPanel maneja SSL
|
||||
```
|
||||
|
||||
### 2. VPS Separado (Solo LiveKit + Redis)
|
||||
```yaml
|
||||
# docker-compose.yml en VPS
|
||||
version: '3.8'
|
||||
services:
|
||||
livekit:
|
||||
image: livekit/livekit-server:latest
|
||||
ports:
|
||||
- "7880:7880" # API/WebSocket
|
||||
- "50000-60000:50000-60000/udp" # WebRTC
|
||||
volumes:
|
||||
- ./livekit.yaml:/livekit.yaml
|
||||
command: --config /livekit.yaml
|
||||
|
||||
redis:
|
||||
image: redis:7-alpine
|
||||
ports:
|
||||
- "6379:6379"
|
||||
command: redis-server --requirepass redispassword
|
||||
```
|
||||
|
||||
### 3. Configuración LiveKit en VPS
|
||||
```yaml
|
||||
# livekit.yaml en VPS
|
||||
port: 7880
|
||||
bind_addresses: ["0.0.0.0"]
|
||||
|
||||
keys:
|
||||
devkey: tu-secret-de-32-caracteres-minimo
|
||||
|
||||
redis:
|
||||
address: "localhost:6379"
|
||||
password: "redispassword"
|
||||
|
||||
rtc:
|
||||
port_range_start: 50000
|
||||
port_range_end: 60000
|
||||
use_external_ip: true
|
||||
external_ip: "IP_PUBLICA_DEL_VPS"
|
||||
|
||||
ice_servers:
|
||||
- urls: ["stun:stun.l.google.com:19302"]
|
||||
```
|
||||
|
||||
### 4. Firewall en VPS
|
||||
```bash
|
||||
# Configurar firewall en VPS
|
||||
ufw allow 7880/tcp # LiveKit API
|
||||
ufw allow 50000:60000/udp # WebRTC UDP
|
||||
ufw allow 6379/tcp # Redis (si acceso externo)
|
||||
ufw enable
|
||||
```
|
||||
|
||||
### 5. SSL para LiveKit (Nginx en VPS)
|
||||
```nginx
|
||||
# /etc/nginx/sites-available/livekit
|
||||
server {
|
||||
listen 443 ssl;
|
||||
server_name livekit.tu-vps.com;
|
||||
|
||||
ssl_certificate /path/to/cert.pem;
|
||||
ssl_certificate_key /path/to/key.pem;
|
||||
|
||||
location / {
|
||||
proxy_pass http://localhost:7880;
|
||||
proxy_http_version 1.1;
|
||||
proxy_set_header Upgrade $http_upgrade;
|
||||
proxy_set_header Connection "upgrade";
|
||||
proxy_set_header Host $host;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## 💰 COSTOS ESTIMADOS
|
||||
|
||||
### VPS para LiveKit:
|
||||
- **Básico**: $5-10/mes (2GB RAM, 1 CPU)
|
||||
- **Medio**: $15-25/mes (4GB RAM, 2 CPU)
|
||||
- **Alto**: $30-50/mes (8GB RAM, 4 CPU)
|
||||
|
||||
### Proveedores recomendados:
|
||||
- DigitalOcean
|
||||
- Linode
|
||||
- Hetzner
|
||||
- Vultr
|
||||
|
||||
## ✅ VENTAJAS
|
||||
- ✅ EasyPanel para web app (fácil)
|
||||
- ✅ VPS dedicado para WebRTC (potencia)
|
||||
- ✅ Escalabilidad independiente
|
||||
- ✅ Control total sobre LiveKit
|
||||
|
||||
## ❌ DESVENTAJAS
|
||||
- ❌ Costo adicional VPS
|
||||
- ❌ Más complejidad de setup
|
||||
- ❌ Mantenimiento de dos servicios
|
||||
@ -1,180 +0,0 @@
|
||||
# SERVIDOR LIVEKIT SELF-HOSTING DEDICADO
|
||||
|
||||
## 🖥️ Setup en servidor dedicado (192.168.1.19)
|
||||
|
||||
### Docker Compose para LiveKit Server:
|
||||
```yaml
|
||||
# docker-compose-livekit-server.yml
|
||||
version: '3.8'
|
||||
|
||||
services:
|
||||
# LiveKit Server Principal
|
||||
livekit-server:
|
||||
image: livekit/livekit-server:latest
|
||||
container_name: livekit-production
|
||||
restart: unless-stopped
|
||||
ports:
|
||||
# API/WebSocket (EXPONER PÚBLICAMENTE)
|
||||
- "7880:7880"
|
||||
|
||||
# Rango UDP para WebRTC (EXPONER PÚBLICAMENTE)
|
||||
- "50000-50100:50000-50100/udp" # 100 puertos para ~10 usuarios concurrentes
|
||||
|
||||
volumes:
|
||||
- ./livekit-production.yaml:/livekit.yaml:ro
|
||||
- ./logs:/app/logs
|
||||
command: --config /livekit.yaml
|
||||
environment:
|
||||
- LIVEKIT_CONFIG=/livekit.yaml
|
||||
networks:
|
||||
- livekit-network
|
||||
depends_on:
|
||||
- redis
|
||||
|
||||
# Redis para LiveKit
|
||||
redis:
|
||||
image: redis:7-alpine
|
||||
container_name: livekit-redis
|
||||
restart: unless-stopped
|
||||
ports:
|
||||
- "6379:6379"
|
||||
command: redis-server --requirepass ${REDIS_PASSWORD:-livekitredis123}
|
||||
volumes:
|
||||
- redis_data:/data
|
||||
networks:
|
||||
- livekit-network
|
||||
|
||||
# Nginx SSL Termination (para HTTPS/WSS)
|
||||
nginx-livekit:
|
||||
image: nginx:alpine
|
||||
container_name: livekit-nginx
|
||||
restart: unless-stopped
|
||||
ports:
|
||||
- "443:443" # HTTPS/WSS (EXPONER PÚBLICAMENTE)
|
||||
- "80:80" # HTTP redirect
|
||||
volumes:
|
||||
- ./nginx-livekit.conf:/etc/nginx/nginx.conf:ro
|
||||
- ./ssl:/etc/nginx/ssl:ro # Certificados SSL
|
||||
depends_on:
|
||||
- livekit-server
|
||||
networks:
|
||||
- livekit-network
|
||||
|
||||
volumes:
|
||||
redis_data:
|
||||
|
||||
networks:
|
||||
livekit-network:
|
||||
driver: bridge
|
||||
```
|
||||
|
||||
### Configuración LiveKit Production:
|
||||
```yaml
|
||||
# livekit-production.yaml
|
||||
port: 7880
|
||||
bind_addresses: ["0.0.0.0"]
|
||||
|
||||
# API Keys seguros
|
||||
keys:
|
||||
production-key: tu-super-secret-de-32-caracteres-o-mas
|
||||
|
||||
# Redis para scaling y persistence
|
||||
redis:
|
||||
address: "redis:6379"
|
||||
password: "livekitredis123"
|
||||
db: 0
|
||||
|
||||
# RTC Configuration para acceso público
|
||||
rtc:
|
||||
# Puertos UDP (coincidir con docker-compose)
|
||||
port_range_start: 50000
|
||||
port_range_end: 50100
|
||||
|
||||
# IP pública/externa (tu IP pública o dominio)
|
||||
use_external_ip: true
|
||||
external_ip: "TU_IP_PUBLICA_O_DOMINIO" # ej: "mi-casa.duckdns.org"
|
||||
|
||||
# STUN servers para NAT traversal
|
||||
ice_servers:
|
||||
- urls: ["stun:stun.l.google.com:19302"]
|
||||
- urls: ["stun:stun1.l.google.com:19302"]
|
||||
|
||||
# Room settings para producción
|
||||
room:
|
||||
auto_create: true
|
||||
max_participants: 50
|
||||
empty_timeout: 600 # 10 minutos
|
||||
|
||||
# Security
|
||||
webhook:
|
||||
# Opcional: webhook para eventos
|
||||
api_key: "tu-webhook-key"
|
||||
|
||||
# Logging
|
||||
log_level: info
|
||||
log_format: json
|
||||
|
||||
# Enable egress (grabaciones)
|
||||
# Automático con Redis
|
||||
```
|
||||
|
||||
### Nginx SSL para LiveKit:
|
||||
```nginx
|
||||
# nginx-livekit.conf
|
||||
events {
|
||||
worker_connections 1024;
|
||||
}
|
||||
|
||||
http {
|
||||
# Redirect HTTP to HTTPS
|
||||
server {
|
||||
listen 80;
|
||||
server_name _;
|
||||
return 301 https://$host$request_uri;
|
||||
}
|
||||
|
||||
# HTTPS/WSS Server
|
||||
server {
|
||||
listen 443 ssl http2;
|
||||
server_name _;
|
||||
|
||||
# SSL Configuration
|
||||
ssl_certificate /etc/nginx/ssl/cert.pem;
|
||||
ssl_certificate_key /etc/nginx/ssl/key.pem;
|
||||
ssl_protocols TLSv1.2 TLSv1.3;
|
||||
ssl_ciphers HIGH:!aNULL:!MD5;
|
||||
|
||||
# WebSocket support para LiveKit
|
||||
location / {
|
||||
proxy_pass http://livekit-server:7880;
|
||||
proxy_http_version 1.1;
|
||||
proxy_set_header Upgrade $http_upgrade;
|
||||
proxy_set_header Connection "upgrade";
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
proxy_set_header X-Forwarded-Proto $scheme;
|
||||
|
||||
# Timeouts para WebRTC
|
||||
proxy_connect_timeout 60s;
|
||||
proxy_send_timeout 60s;
|
||||
proxy_read_timeout 60s;
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## 🔥 Firewall en servidor LiveKit:
|
||||
```bash
|
||||
# UFW rules para exposición pública segura
|
||||
sudo ufw allow 80/tcp comment "HTTP redirect"
|
||||
sudo ufw allow 443/tcp comment "HTTPS/WSS LiveKit"
|
||||
sudo ufw allow 7880/tcp comment "LiveKit API directo"
|
||||
sudo ufw allow 50000:50100/udp comment "WebRTC UDP range"
|
||||
|
||||
# Opcional: limitar SSH a red local solamente
|
||||
sudo ufw allow from 192.168.1.0/24 to any port 22
|
||||
|
||||
sudo ufw enable
|
||||
sudo ufw status numbered
|
||||
```
|
||||
@ -1,40 +0,0 @@
|
||||
# CONFIGURACIÓN TURN PARA LIVEKIT
|
||||
|
||||
## 🔧 Si necesitas TURN server para LiveKit detrás de firewall:
|
||||
|
||||
### 1. Configurar Coturn (TURN server)
|
||||
```bash
|
||||
# Instalar coturn
|
||||
apt-get install coturn
|
||||
|
||||
# /etc/turnserver.conf
|
||||
listening-port=3478
|
||||
tls-listening-port=5349
|
||||
external-ip=TU_IP_PUBLICA
|
||||
realm=tu-dominio.com
|
||||
lt-cred-mech
|
||||
user=usuario:password
|
||||
```
|
||||
|
||||
### 2. Configurar LiveKit con TURN
|
||||
```yaml
|
||||
# livekit.yaml
|
||||
rtc:
|
||||
port_range_start: 50000
|
||||
port_range_end: 60000
|
||||
ice_servers:
|
||||
- urls:
|
||||
- "stun:stun.l.google.com:19302"
|
||||
- "turn:tu-turn-server.com:3478"
|
||||
username: "usuario"
|
||||
credential: "password"
|
||||
```
|
||||
|
||||
### 3. Firewall para TURN
|
||||
```bash
|
||||
# Puertos necesarios
|
||||
ufw allow 3478/tcp # TURN TCP
|
||||
ufw allow 3478/udp # TURN UDP
|
||||
ufw allow 5349/tcp # TURN TLS
|
||||
ufw allow 50000:60000/udp # Media streams
|
||||
```
|
||||
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)
|
||||
3. [Getting Started](#getting-started)
|
||||
4. [Development](#development)
|
||||
- [Development Mode](#development-mode)
|
||||
- [Manual Development Setup](#manual-development-setup)
|
||||
- [Development Mode](#development-mode)
|
||||
- [Manual Development Setup](#manual-development-setup)
|
||||
5. [Building](#building)
|
||||
6. [Testing](#testing)
|
||||
7. [Documentation](#documentation)
|
||||
@ -26,12 +26,12 @@ The OpenVidu Meet application is a monorepo managed with **pnpm workspaces** and
|
||||
### Core Components
|
||||
|
||||
- **Frontend** (`frontend/`): Angular 20 application providing the user interface
|
||||
- **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
|
||||
- **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
|
||||
|
||||
- **Backend** (`backend/`): Node.js/TypeScript REST API server
|
||||
- Manages rooms, participants, recordings, and authentication
|
||||
- Serves the compiled frontend in production
|
||||
- Manages rooms, participants, recordings, and authentication
|
||||
- Serves the compiled frontend in production
|
||||
|
||||
- **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
|
||||
- **pnpm**: Package manager (will be installed automatically by meet.sh if missing)
|
||||
- **LiveKit**: For local testing (optional)
|
||||
```bash
|
||||
curl -sSL https://get.livekit.io/cli | bash
|
||||
```
|
||||
|
||||
```bash
|
||||
curl -sSL https://get.livekit.io/cli | bash
|
||||
```
|
||||
|
||||
## Getting Started
|
||||
|
||||
@ -82,7 +81,7 @@ cd openvidu-meet
|
||||
|
||||
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
|
||||
|
||||
@ -95,6 +94,7 @@ The recommended way to develop is using the integrated development mode that wat
|
||||
```
|
||||
|
||||
This command starts concurrent watchers for:
|
||||
|
||||
- **openvidu-components-angular**: Core Angular components library
|
||||
- **Typings**: Shared type definitions with automatic sync
|
||||
- **Backend**: Node.js server with nodemon auto-restart
|
||||
@ -103,6 +103,7 @@ This command starts concurrent watchers for:
|
||||
|
||||
> [!NOTE]
|
||||
> The backend uses `backend/.env.development` for environment variables during development. Configure your LiveKit credentials there:
|
||||
>
|
||||
> ```env
|
||||
> LIVEKIT_URL=ws://localhost:7880
|
||||
> LIVEKIT_API_KEY=your-api-key
|
||||
@ -170,6 +171,7 @@ The `meet.sh` script supports flags to optimize CI/CD pipelines:
|
||||
```
|
||||
|
||||
**Available flags:**
|
||||
|
||||
- `--skip-install`: Skip dependency installation
|
||||
- `--skip-build`: Skip build steps
|
||||
- `--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:
|
||||
|
||||
- **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.
|
||||
|
||||
@ -289,35 +292,41 @@ openvidu-meet/
|
||||
│ ├── src/
|
||||
│ │ ├── api-key.ts
|
||||
│ │ ├── auth-config.ts
|
||||
│ │ ├── participant.ts
|
||||
│ │ ├── event.model.ts
|
||||
│ │ ├── room.ts
|
||||
│ │ └── ...
|
||||
│ └── package.json
|
||||
│
|
||||
├── frontend/ # Angular frontend application
|
||||
├── frontend/ # Angular frontend application
|
||||
│ ├── src/ # Main application source
|
||||
│ ├── projects/
|
||||
│ │ └── shared-meet-components/ # Reusable Angular library
|
||||
│ └── webcomponent/ # Web component build
|
||||
│
|
||||
├── backend/ # Node.js/Express backend
|
||||
├── backend/ # Node.js/Express backend
|
||||
│ ├── src/
|
||||
│ │ ├── config/ # Configuration files
|
||||
│ │ ├── controllers/ # REST API controllers
|
||||
│ │ ├── services/ # Business logic
|
||||
│ │ ├── helpers/ # Helper functions
|
||||
│ │ ├── 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
|
||||
│ ├── openapi/ # OpenAPI specifications
|
||||
│ └── public/ # Static files (includes built frontend)
|
||||
│
|
||||
├── testapp/ # Testing application
|
||||
├── testapp/ # Testing application
|
||||
│ ├── src/
|
||||
│ └── public/
|
||||
│
|
||||
├── docker/ # Docker build files
|
||||
├── docker/ # Docker build files
|
||||
│ └── create_image.sh
|
||||
│
|
||||
├── docs/ # Generated documentation
|
||||
├── scripts/ # Build and utility scripts
|
||||
├── docs/ # Generated documentation
|
||||
├── scripts/ # Build and utility scripts
|
||||
└── 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 Meet](https://openvidu.io/latest/meet/)
|
||||
|
||||
---
|
||||
|
||||
For questions and support, visit our [community forum](https://openvidu.discourse.group/).
|
||||
|
||||
@ -1,103 +0,0 @@
|
||||
# CONFIGURACIÓN ROUTER - PORT FORWARDING PARA LIVEKIT
|
||||
|
||||
## 🌐 Port Forwarding necesario en tu Router
|
||||
|
||||
### Puertos a exponer públicamente:
|
||||
|
||||
| Servicio | Puerto | Protocolo | IP Interna | Descripción |
|
||||
|----------|---------|-----------|------------|-------------|
|
||||
| **HTTP** | 80 | TCP | 192.168.1.19 | Redirect a HTTPS |
|
||||
| **HTTPS/WSS** | 443 | TCP | 192.168.1.19 | LiveKit WebSocket Secure |
|
||||
| **LiveKit API** | 7880 | TCP | 192.168.1.19 | API directa (opcional) |
|
||||
| **WebRTC Media** | 50000-50100 | UDP | 192.168.1.19 | Streams de audio/video |
|
||||
|
||||
### Configuración típica router:
|
||||
|
||||
```
|
||||
Regla 1: LiveKit-HTTPS
|
||||
- Servicio: HTTPS/Custom
|
||||
- Puerto externo: 443
|
||||
- Puerto interno: 443
|
||||
- IP interna: 192.168.1.19
|
||||
- Protocolo: TCP
|
||||
- Estado: Habilitado
|
||||
|
||||
Regla 2: LiveKit-HTTP
|
||||
- Servicio: HTTP
|
||||
- Puerto externo: 80
|
||||
- Puerto interno: 80
|
||||
- IP interna: 192.168.1.19
|
||||
- Protocolo: TCP
|
||||
- Estado: Habilitado
|
||||
|
||||
Regla 3: LiveKit-WebRTC
|
||||
- Servicio: Custom
|
||||
- Puerto externo: 50000-50100
|
||||
- Puerto interno: 50000-50100
|
||||
- IP interna: 192.168.1.19
|
||||
- Protocolo: UDP
|
||||
- Estado: Habilitado
|
||||
```
|
||||
|
||||
## 🏠 IP Dinámica - Solución con DuckDNS
|
||||
|
||||
### Si tu IP pública cambia (típico en casa):
|
||||
|
||||
```bash
|
||||
# 1. Crear cuenta en DuckDNS.org
|
||||
# 2. Crear subdominio: mi-livekit.duckdns.org
|
||||
# 3. Script de actualización automática
|
||||
|
||||
# /home/usuario/update-duckdns.sh
|
||||
#!/bin/bash
|
||||
echo url="https://www.duckdns.org/update?domains=mi-livekit&token=TU_TOKEN&ip=" | curl -k -o ~/duckdns.log -K -
|
||||
|
||||
# Crontab para actualizar cada 5 minutos
|
||||
# crontab -e
|
||||
*/5 * * * * /home/usuario/update-duckdns.sh >/dev/null 2>&1
|
||||
```
|
||||
|
||||
### Configurar dominio en LiveKit:
|
||||
```yaml
|
||||
# livekit-production.yaml
|
||||
rtc:
|
||||
external_ip: "mi-livekit.duckdns.org" # En lugar de IP
|
||||
```
|
||||
|
||||
## 🔒 Certificado SSL automático con Let's Encrypt
|
||||
|
||||
```bash
|
||||
# Instalar certbot
|
||||
sudo apt install certbot
|
||||
|
||||
# Generar certificado para tu dominio
|
||||
sudo certbot certonly --standalone -d mi-livekit.duckdns.org
|
||||
|
||||
# Copiar certificados para Docker
|
||||
sudo cp /etc/letsencrypt/live/mi-livekit.duckdns.org/fullchain.pem ./ssl/cert.pem
|
||||
sudo cp /etc/letsencrypt/live/mi-livekit.duckdns.org/privkey.pem ./ssl/key.pem
|
||||
sudo chown $USER:$USER ./ssl/*.pem
|
||||
|
||||
# Auto-renovación (crontab)
|
||||
0 12 * * * /usr/bin/certbot renew --quiet && docker-compose restart nginx-livekit
|
||||
```
|
||||
|
||||
## 📊 Verificación de conectividad
|
||||
|
||||
### Tests externos:
|
||||
```bash
|
||||
# Test puertos desde internet
|
||||
nmap -p 80,443,7880 mi-livekit.duckdns.org
|
||||
nmap -sU -p 50000-50010 mi-livekit.duckdns.org
|
||||
|
||||
# Test WebSocket
|
||||
wscat -c wss://mi-livekit.duckdns.org
|
||||
|
||||
# Test HTTPS
|
||||
curl -I https://mi-livekit.duckdns.org
|
||||
```
|
||||
|
||||
### URLs finales:
|
||||
- **LiveKit WSS**: `wss://mi-livekit.duckdns.org`
|
||||
- **API HTTP**: `https://mi-livekit.duckdns.org`
|
||||
- **Monitoreo**: `https://mi-livekit.duckdns.org/debug`
|
||||
@ -1,106 +0,0 @@
|
||||
# CONFIGURACIÓN MANUAL DE PUERTOS UDP PARA LIVEKIT
|
||||
|
||||
## 🔥 FIREWALL UBUNTU/DEBIAN (UFW)
|
||||
```bash
|
||||
# Puertos TCP
|
||||
sudo ufw allow 80/tcp comment "HTTP"
|
||||
sudo ufw allow 6080/tcp comment "OpenVidu Meet"
|
||||
sudo ufw allow 6379/tcp comment "Redis"
|
||||
sudo ufw allow 7880/tcp comment "LiveKit API"
|
||||
|
||||
# Puertos UDP para WebRTC
|
||||
sudo ufw allow 50000:60000/udp comment "LiveKit WebRTC"
|
||||
|
||||
# Verificar
|
||||
sudo ufw status numbered
|
||||
```
|
||||
|
||||
## 🔥 FIREWALL CENTOS/RHEL (firewalld)
|
||||
```bash
|
||||
# Puertos TCP
|
||||
sudo firewall-cmd --permanent --add-port=80/tcp
|
||||
sudo firewall-cmd --permanent --add-port=6080/tcp
|
||||
sudo firewall-cmd --permanent --add-port=6379/tcp
|
||||
sudo firewall-cmd --permanent --add-port=7880/tcp
|
||||
|
||||
# Puertos UDP
|
||||
sudo firewall-cmd --permanent --add-port=50000-60000/udp
|
||||
|
||||
# Aplicar
|
||||
sudo firewall-cmd --reload
|
||||
sudo firewall-cmd --list-ports
|
||||
```
|
||||
|
||||
## 🖥️ ROUTER/MODEM (Para acceso externo)
|
||||
Si quieres acceso desde internet:
|
||||
|
||||
### Port Forwarding necesario:
|
||||
- **TCP 80** → Tu servidor (OpenVidu Meet)
|
||||
- **TCP 7880** → Tu servidor (LiveKit API)
|
||||
- **UDP 50000-60000** → Tu servidor (WebRTC media)
|
||||
|
||||
### Configuración típica router:
|
||||
```
|
||||
Servicio: OpenVidu-HTTP
|
||||
Puerto externo: 80
|
||||
Puerto interno: 80
|
||||
IP interna: 192.168.1.19
|
||||
Protocolo: TCP
|
||||
|
||||
Servicio: LiveKit-API
|
||||
Puerto externo: 7880
|
||||
Puerto interno: 7880
|
||||
IP interna: 192.168.1.19
|
||||
Protocolo: TCP
|
||||
|
||||
Servicio: WebRTC-Media
|
||||
Puerto externo: 50000-60000
|
||||
Puerto interno: 50000-60000
|
||||
IP interna: 192.168.1.19
|
||||
Protocolo: UDP
|
||||
```
|
||||
|
||||
## 🔍 VERIFICACIÓN DE PUERTOS
|
||||
|
||||
### Verificar puertos abiertos:
|
||||
```bash
|
||||
# Ver todos los puertos TCP/UDP en uso
|
||||
sudo ss -tulnp
|
||||
|
||||
# Específicos de LiveKit
|
||||
sudo ss -tulnp | grep -E "(7880|50000|60000)"
|
||||
|
||||
# Verificar desde otro dispositivo
|
||||
nmap -p 7880,50000-50010 192.168.1.19
|
||||
```
|
||||
|
||||
### Test de conectividad:
|
||||
```bash
|
||||
# Test TCP (LiveKit API)
|
||||
curl http://192.168.1.19:7880
|
||||
|
||||
# Test WebSocket
|
||||
wscat -c ws://192.168.1.19:7880
|
||||
|
||||
# Test UDP (requiere herramientas específicas)
|
||||
nc -u 192.168.1.19 50000
|
||||
```
|
||||
|
||||
## ⚠️ CONSIDERACIONES IMPORTANTES
|
||||
|
||||
### Para red local:
|
||||
- ✅ Solo configurar firewall del servidor
|
||||
- ✅ Usar IP local (192.168.x.x)
|
||||
- ✅ No necesita port forwarding
|
||||
|
||||
### Para acceso externo:
|
||||
- ⚠️ Configurar port forwarding en router
|
||||
- ⚠️ Usar IP pública o dominio
|
||||
- ⚠️ Configurar HTTPS para LiveKit
|
||||
- ⚠️ Considerar seguridad (VPN, etc.)
|
||||
|
||||
### Rango de puertos UDP:
|
||||
- **Mínimo:** 100 puertos (ej: 50000-50100)
|
||||
- **Recomendado:** 1000 puertos (50000-51000)
|
||||
- **Máximo configurado:** 10000 puertos (50000-60000)
|
||||
- **Cálculo:** ~10 puertos por participante simultáneo
|
||||
@ -1,190 +0,0 @@
|
||||
#!/bin/bash
|
||||
# Script para configurar dominio automáticamente para LiveKit
|
||||
|
||||
set -e
|
||||
|
||||
# Colores
|
||||
RED='\033[0;31m'
|
||||
GREEN='\033[0;32m'
|
||||
YELLOW='\033[1;33m'
|
||||
BLUE='\033[0;34m'
|
||||
NC='\033[0m'
|
||||
|
||||
echo -e "${BLUE}🌐 Configurador de Dominio para LiveKit${NC}"
|
||||
echo ""
|
||||
|
||||
# Detectar IP pública
|
||||
echo "🔍 Detectando IP pública..."
|
||||
PUBLIC_IP=$(curl -s https://checkip.amazonaws.com || curl -s https://ipinfo.io/ip || echo "No detectada")
|
||||
LOCAL_IP=$(hostname -I | awk '{print $1}')
|
||||
|
||||
echo -e "${BLUE}📊 Información de red:${NC}"
|
||||
echo " IP Local: $LOCAL_IP"
|
||||
echo " IP Pública: $PUBLIC_IP"
|
||||
echo ""
|
||||
|
||||
# Opciones de dominio
|
||||
echo "¿Qué tipo de dominio quieres configurar?"
|
||||
echo "1) DuckDNS (gratuito, IP dinámica)"
|
||||
echo "2) Dominio propio + Let's Encrypt"
|
||||
echo "3) Solo IP pública (sin dominio)"
|
||||
echo ""
|
||||
read -p "Selecciona opción (1-3): " DOMAIN_OPTION
|
||||
|
||||
case $DOMAIN_OPTION in
|
||||
1)
|
||||
echo -e "${GREEN}🦆 Configurando DuckDNS${NC}"
|
||||
read -p "Subdominio DuckDNS (sin .duckdns.org): " DUCKDNS_SUBDOMAIN
|
||||
read -p "Token DuckDNS: " DUCKDNS_TOKEN
|
||||
|
||||
DOMAIN="$DUCKDNS_SUBDOMAIN.duckdns.org"
|
||||
|
||||
# Crear script de actualización
|
||||
cat > update-duckdns.sh << EOF
|
||||
#!/bin/bash
|
||||
CURRENT_IP=\$(curl -s https://checkip.amazonaws.com)
|
||||
RESPONSE=\$(curl -s "https://www.duckdns.org/update?domains=$DUCKDNS_SUBDOMAIN&token=$DUCKDNS_TOKEN&ip=\$CURRENT_IP")
|
||||
if [ "\$RESPONSE" = "OK" ]; then
|
||||
echo "\$(date): DuckDNS actualizado - $DOMAIN → \$CURRENT_IP"
|
||||
else
|
||||
echo "\$(date): ERROR: \$RESPONSE"
|
||||
fi
|
||||
EOF
|
||||
chmod +x update-duckdns.sh
|
||||
|
||||
# Actualizar inmediatamente
|
||||
./update-duckdns.sh
|
||||
|
||||
# Configurar cron
|
||||
(crontab -l 2>/dev/null; echo "*/5 * * * * $(pwd)/update-duckdns.sh >> $(pwd)/duckdns.log 2>&1") | crontab -
|
||||
|
||||
echo -e "${GREEN}✅ DuckDNS configurado: $DOMAIN${NC}"
|
||||
LIVEKIT_URL="ws://$DOMAIN:7880"
|
||||
;;
|
||||
|
||||
2)
|
||||
echo -e "${GREEN}🏠 Configurando dominio propio${NC}"
|
||||
read -p "Dominio completo (ej: livekit.midominio.com): " CUSTOM_DOMAIN
|
||||
|
||||
DOMAIN="$CUSTOM_DOMAIN"
|
||||
|
||||
echo -e "${YELLOW}📋 Pasos manuales necesarios:${NC}"
|
||||
echo "1. Configurar DNS A record:"
|
||||
echo " $DOMAIN → $PUBLIC_IP"
|
||||
echo ""
|
||||
echo "2. Port forwarding en router:"
|
||||
echo " TCP 80,443,7880 → $LOCAL_IP"
|
||||
echo " UDP 50000-50100 → $LOCAL_IP"
|
||||
echo ""
|
||||
|
||||
read -p "¿Continuar con configuración SSL automática? (y/N): " SSL_SETUP
|
||||
|
||||
if [[ $SSL_SETUP =~ ^[Yy]$ ]]; then
|
||||
echo -e "${YELLOW}🔧 Configurando Nginx + SSL...${NC}"
|
||||
|
||||
# Instalar dependencias
|
||||
sudo apt update
|
||||
sudo apt install -y nginx certbot python3-certbot-nginx
|
||||
|
||||
# Configurar Nginx básico
|
||||
sudo tee /etc/nginx/sites-available/livekit << EOF
|
||||
server {
|
||||
listen 80;
|
||||
server_name $DOMAIN;
|
||||
|
||||
location /.well-known/acme-challenge/ {
|
||||
root /var/www/html;
|
||||
}
|
||||
|
||||
location / {
|
||||
return 301 https://\$server_name\$request_uri;
|
||||
}
|
||||
}
|
||||
EOF
|
||||
|
||||
sudo ln -sf /etc/nginx/sites-available/livekit /etc/nginx/sites-enabled/
|
||||
sudo nginx -t && sudo systemctl restart nginx
|
||||
|
||||
# Generar certificado
|
||||
sudo certbot --nginx -d $DOMAIN --non-interactive --agree-tos --email admin@$DOMAIN
|
||||
|
||||
echo -e "${GREEN}✅ SSL configurado para $DOMAIN${NC}"
|
||||
fi
|
||||
|
||||
LIVEKIT_URL="wss://$DOMAIN"
|
||||
;;
|
||||
|
||||
3)
|
||||
echo -e "${YELLOW}📡 Usando IP pública directa${NC}"
|
||||
DOMAIN="$PUBLIC_IP"
|
||||
LIVEKIT_URL="ws://$PUBLIC_IP:7880"
|
||||
;;
|
||||
|
||||
*)
|
||||
echo -e "${RED}❌ Opción inválida${NC}"
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
|
||||
# Actualizar configuración LiveKit
|
||||
echo -e "${YELLOW}🔧 Actualizando configuración LiveKit...${NC}"
|
||||
|
||||
# Actualizar livekit.yaml existente o crear nuevo
|
||||
if [ -f "livekit-production.yaml" ]; then
|
||||
sed -i "s/external_ip: .*/external_ip: \"$DOMAIN\"/" livekit-production.yaml
|
||||
echo -e "${GREEN}✅ livekit-production.yaml actualizado${NC}"
|
||||
elif [ -f "livekit.yaml" ]; then
|
||||
sed -i "s/external_ip: .*/external_ip: \"$DOMAIN\"/" livekit.yaml
|
||||
echo -e "${GREEN}✅ livekit.yaml actualizado${NC}"
|
||||
else
|
||||
echo -e "${YELLOW}⚠️ No se encontró archivo de configuración LiveKit${NC}"
|
||||
fi
|
||||
|
||||
# Actualizar variables para OpenVidu Meet
|
||||
cat > .env.livekit-domain << EOF
|
||||
# Configuración de dominio para LiveKit
|
||||
DOMAIN=$DOMAIN
|
||||
LIVEKIT_URL=$LIVEKIT_URL
|
||||
PUBLIC_IP=$PUBLIC_IP
|
||||
LOCAL_IP=$LOCAL_IP
|
||||
|
||||
# Variables para EasyPanel/OpenVidu Meet:
|
||||
LIVEKIT_URL=$LIVEKIT_URL
|
||||
LIVEKIT_API_KEY=production-key
|
||||
LIVEKIT_API_SECRET=tu-secret-de-32-caracteres
|
||||
EOF
|
||||
|
||||
echo -e "${GREEN}"
|
||||
echo "============================================="
|
||||
echo "🎉 DOMINIO CONFIGURADO EXITOSAMENTE"
|
||||
echo "============================================="
|
||||
echo "🌐 Dominio: $DOMAIN"
|
||||
echo "🔗 LiveKit URL: $LIVEKIT_URL"
|
||||
echo "📍 IP Pública: $PUBLIC_IP"
|
||||
echo "🏠 IP Local: $LOCAL_IP"
|
||||
echo ""
|
||||
echo "📋 CONFIGURACIÓN PARA OPENVIDU MEET:"
|
||||
echo " LIVEKIT_URL=$LIVEKIT_URL"
|
||||
echo ""
|
||||
echo "🔧 PUERTOS NECESARIOS EN ROUTER:"
|
||||
echo " TCP 7880 → $LOCAL_IP:7880"
|
||||
echo " UDP 50000-50100 → $LOCAL_IP:50000-50100"
|
||||
if [[ $DOMAIN_OPTION == 2 ]]; then
|
||||
echo " TCP 80,443 → $LOCAL_IP:80,443"
|
||||
fi
|
||||
echo ""
|
||||
echo "📁 Archivos generados:"
|
||||
echo " - .env.livekit-domain (variables)"
|
||||
if [[ $DOMAIN_OPTION == 1 ]]; then
|
||||
echo " - update-duckdns.sh (actualización automática)"
|
||||
fi
|
||||
echo "============================================="
|
||||
echo -e "${NC}"
|
||||
|
||||
# Test conectividad
|
||||
echo -e "${BLUE}🔍 Probando conectividad...${NC}"
|
||||
if timeout 5 bash -c "echo >/dev/tcp/$DOMAIN/7880" 2>/dev/null; then
|
||||
echo -e "${GREEN}✅ Puerto 7880 accesible${NC}"
|
||||
else
|
||||
echo -e "${YELLOW}⚠️ Puerto 7880 no accesible (verificar port forwarding)${NC}"
|
||||
fi
|
||||
@ -1,58 +0,0 @@
|
||||
#!/bin/bash
|
||||
# Script para configurar puertos UDP para LiveKit local
|
||||
|
||||
echo "🔧 Configurando puertos UDP para LiveKit..."
|
||||
|
||||
# Verificar si ufw está disponible
|
||||
if command -v ufw &> /dev/null; then
|
||||
echo "Configurando con UFW..."
|
||||
|
||||
# Puertos TCP para LiveKit API
|
||||
sudo ufw allow 7880/tcp comment "LiveKit API"
|
||||
|
||||
# Rango de puertos UDP para WebRTC (según livekit.yaml)
|
||||
sudo ufw allow 50000:60000/udp comment "LiveKit WebRTC UDP"
|
||||
|
||||
# Verificar reglas
|
||||
echo "Reglas UFW configuradas:"
|
||||
sudo ufw status numbered
|
||||
|
||||
elif command -v firewall-cmd &> /dev/null; then
|
||||
echo "Configurando con firewalld..."
|
||||
|
||||
# Puerto TCP para LiveKit
|
||||
sudo firewall-cmd --permanent --add-port=7880/tcp
|
||||
|
||||
# Rango UDP para WebRTC
|
||||
sudo firewall-cmd --permanent --add-port=50000-60000/udp
|
||||
|
||||
# Recargar firewall
|
||||
sudo firewall-cmd --reload
|
||||
|
||||
echo "Reglas firewalld configuradas:"
|
||||
sudo firewall-cmd --list-ports
|
||||
|
||||
else
|
||||
echo "⚠️ No se detectó UFW ni firewalld"
|
||||
echo "Configurar manualmente:"
|
||||
echo "- TCP 7880 (LiveKit API)"
|
||||
echo "- UDP 50000-60000 (WebRTC media)"
|
||||
fi
|
||||
|
||||
echo "✅ Configuración de firewall completada"
|
||||
|
||||
# Verificar que LiveKit esté corriendo
|
||||
echo "🔍 Verificando LiveKit..."
|
||||
if curl -s http://localhost:7880 > /dev/null 2>&1; then
|
||||
echo "✅ LiveKit responde en puerto 7880"
|
||||
else
|
||||
echo "❌ LiveKit no responde - verificar que esté corriendo"
|
||||
fi
|
||||
|
||||
# Mostrar puertos abiertos
|
||||
echo "📊 Puertos actualmente en uso:"
|
||||
if command -v ss &> /dev/null; then
|
||||
ss -tulnp | grep -E "(7880|50000|60000)"
|
||||
elif command -v netstat &> /dev/null; then
|
||||
netstat -tulnp | grep -E "(7880|50000|60000)"
|
||||
fi
|
||||
@ -1,136 +0,0 @@
|
||||
#!/bin/bash
|
||||
# Script para configurar UDP de forma segura según el caso de uso
|
||||
|
||||
set -e
|
||||
|
||||
# Colores
|
||||
RED='\033[0;31m'
|
||||
GREEN='\033[0;32m'
|
||||
YELLOW='\033[1;33m'
|
||||
BLUE='\033[0;34m'
|
||||
NC='\033[0m'
|
||||
|
||||
echo -e "${BLUE}🔧 Configuración segura de puertos UDP para LiveKit${NC}"
|
||||
echo ""
|
||||
|
||||
# Preguntar caso de uso
|
||||
echo "¿Cuál es tu caso de uso?"
|
||||
echo "1) Solo red local (recomendado y seguro)"
|
||||
echo "2) Acceso desde internet (complejo y riesgoso)"
|
||||
echo "3) Mostrar configuración actual"
|
||||
echo ""
|
||||
read -p "Selecciona opción (1-3): " OPTION
|
||||
|
||||
case $OPTION in
|
||||
1)
|
||||
echo -e "${GREEN}✅ Configurando para RED LOCAL únicamente${NC}"
|
||||
|
||||
# Configurar firewall para solo red local
|
||||
if command -v ufw &> /dev/null; then
|
||||
echo "Configurando UFW para red local..."
|
||||
|
||||
# Permitir desde red local
|
||||
sudo ufw allow from 192.168.0.0/16 to any port 50000:60000 proto udp comment "LiveKit UDP (red local)"
|
||||
sudo ufw allow from 192.168.0.0/16 to any port 7880 proto tcp comment "LiveKit API (red local)"
|
||||
sudo ufw allow from 192.168.0.0/16 to any port 80 proto tcp comment "HTTP (red local)"
|
||||
|
||||
# DENEGAR acceso externo a UDP
|
||||
sudo ufw deny 50000:60000/udp comment "BLOQUEAR UDP externo"
|
||||
|
||||
echo -e "${GREEN}✅ Firewall configurado para red local${NC}"
|
||||
sudo ufw status numbered
|
||||
|
||||
else
|
||||
echo -e "${YELLOW}⚠️ UFW no disponible. Configurar manualmente:${NC}"
|
||||
echo "- Permitir UDP 50000-60000 desde 192.168.x.x"
|
||||
echo "- BLOQUEAR UDP desde internet"
|
||||
fi
|
||||
|
||||
echo ""
|
||||
echo -e "${GREEN}🔒 CONFIGURACIÓN SEGURA APLICADA:${NC}"
|
||||
echo "- UDP 50000-60000: Solo red local"
|
||||
echo "- Acceso web: http://192.168.1.19"
|
||||
echo "- Sin port forwarding necesario"
|
||||
echo "- Máxima seguridad"
|
||||
;;
|
||||
|
||||
2)
|
||||
echo -e "${RED}⚠️ CONFIGURACIÓN PARA ACCESO PÚBLICO${NC}"
|
||||
echo ""
|
||||
echo -e "${YELLOW}RIESGOS:${NC}"
|
||||
echo "- 10,000 puertos UDP expuestos"
|
||||
echo "- Posibles ataques de red"
|
||||
echo "- Configuración compleja"
|
||||
echo "- Problemas con NAT/CGNAT"
|
||||
echo ""
|
||||
echo -e "${BLUE}ALTERNATIVAS RECOMENDADAS:${NC}"
|
||||
echo "1. LiveKit Cloud (sin UDP local)"
|
||||
echo "2. VPN para usuarios remotos"
|
||||
echo "3. TURN server para NAT traversal"
|
||||
echo ""
|
||||
read -p "¿Continuar con configuración pública? (y/N): " -n 1 -r
|
||||
echo
|
||||
|
||||
if [[ $REPLY =~ ^[Yy]$ ]]; then
|
||||
echo -e "${RED}Configurando acceso público...${NC}"
|
||||
|
||||
if command -v ufw &> /dev/null; then
|
||||
# Abrir UDP para todo el mundo (PELIGROSO)
|
||||
sudo ufw allow 50000:60000/udp comment "LiveKit UDP PUBLICO"
|
||||
sudo ufw allow 7880/tcp comment "LiveKit API PUBLICO"
|
||||
sudo ufw allow 80/tcp comment "HTTP PUBLICO"
|
||||
|
||||
echo -e "${RED}❌ UDP EXPUESTO PÚBLICAMENTE${NC}"
|
||||
fi
|
||||
|
||||
echo ""
|
||||
echo -e "${RED}⚠️ CONFIGURACIÓN APLICADA (RIESGOSA):${NC}"
|
||||
echo "- UDP 50000-60000: PÚBLICO"
|
||||
echo "- Configurar port forwarding en router"
|
||||
echo "- Usar IP pública en livekit.yaml"
|
||||
echo "- Considerar VPN o LiveKit Cloud"
|
||||
|
||||
else
|
||||
echo -e "${GREEN}✅ Configuración pública cancelada${NC}"
|
||||
fi
|
||||
;;
|
||||
|
||||
3)
|
||||
echo -e "${BLUE}📊 Configuración actual:${NC}"
|
||||
|
||||
# Verificar puertos UDP
|
||||
echo ""
|
||||
echo "Puertos UDP en uso:"
|
||||
if command -v ss &> /dev/null; then
|
||||
ss -ulnp | grep -E ":(5[0-9]{4})" | head -10
|
||||
fi
|
||||
|
||||
# Verificar firewall
|
||||
echo ""
|
||||
echo "Reglas de firewall:"
|
||||
if command -v ufw &> /dev/null; then
|
||||
sudo ufw status numbered | grep -E "(50000|7880|80)"
|
||||
fi
|
||||
|
||||
# Verificar IP externa
|
||||
echo ""
|
||||
echo "IP externa detectada:"
|
||||
curl -s ifconfig.me || echo "No disponible"
|
||||
|
||||
echo ""
|
||||
echo "IP local:"
|
||||
hostname -I | awk '{print $1}'
|
||||
;;
|
||||
|
||||
*)
|
||||
echo -e "${RED}❌ Opción inválida${NC}"
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
|
||||
echo ""
|
||||
echo -e "${BLUE}💡 RECOMENDACIÓN FINAL:${NC}"
|
||||
echo "Para máxima seguridad y simplicidad:"
|
||||
echo "- Usar solo en red local"
|
||||
echo "- Para acceso remoto: VPN o LiveKit Cloud"
|
||||
echo "- NO exponer 10,000 puertos UDP públicamente"
|
||||
@ -1,100 +0,0 @@
|
||||
#!/bin/bash
|
||||
set -e
|
||||
|
||||
echo "🚀 PREPARANDO DEPLOY PARA EASYPANEL"
|
||||
echo "==================================="
|
||||
|
||||
# 1. Crear directorio ssl
|
||||
echo "1. Creando estructura de directorios..."
|
||||
mkdir -p ssl logs
|
||||
|
||||
# 2. Crear certificados dummy (EasyPanel los reemplazará)
|
||||
echo "2. Creando certificados dummy..."
|
||||
if [ ! -f ssl/cert.pem ]; then
|
||||
openssl req -x509 -nodes -days 365 -newkey rsa:2048 \
|
||||
-keyout ssl/key.pem \
|
||||
-out ssl/cert.pem \
|
||||
-subj "/C=US/ST=State/L=City/O=Organization/CN=localhost" 2>/dev/null || \
|
||||
echo "⚠️ OpenSSL no disponible - EasyPanel manejará SSL"
|
||||
fi
|
||||
|
||||
# 3. Crear .dockerignore
|
||||
echo "3. Optimizando build..."
|
||||
cat > .dockerignore << 'EOF'
|
||||
node_modules
|
||||
.git
|
||||
.env*
|
||||
*.log
|
||||
logs/
|
||||
ssl/
|
||||
*.md
|
||||
.vscode
|
||||
.idea
|
||||
dist
|
||||
coverage
|
||||
.nyc_output
|
||||
EOF
|
||||
|
||||
# 4. Verificar archivos necesarios
|
||||
echo "4. Verificando archivos..."
|
||||
REQUIRED_FILES=(
|
||||
"Dockerfile"
|
||||
"docker-compose.yml"
|
||||
"nginx.conf"
|
||||
".env.production"
|
||||
)
|
||||
|
||||
for file in "${REQUIRED_FILES[@]}"; do
|
||||
if [ -f "$file" ]; then
|
||||
echo " ✅ $file"
|
||||
else
|
||||
echo " ❌ $file - FALTANTE"
|
||||
exit 1
|
||||
fi
|
||||
done
|
||||
|
||||
# 5. Test de build local (opcional)
|
||||
echo "5. ¿Quieres probar el build localmente? (y/n)"
|
||||
read -r TEST_BUILD
|
||||
|
||||
if [ "$TEST_BUILD" = "y" ] || [ "$TEST_BUILD" = "Y" ]; then
|
||||
echo "Construyendo imagen de prueba..."
|
||||
docker build -t openvidu-meet-test . || {
|
||||
echo "❌ Error en el build - revisar Dockerfile"
|
||||
exit 1
|
||||
}
|
||||
echo "✅ Build exitoso"
|
||||
fi
|
||||
|
||||
echo ""
|
||||
echo "🎉 PREPARACIÓN COMPLETADA"
|
||||
echo "========================="
|
||||
echo ""
|
||||
echo "📋 PASOS PARA EASYPANEL:"
|
||||
echo ""
|
||||
echo "1. Crear nuevo proyecto en EasyPanel"
|
||||
echo "2. Conectar repositorio Git"
|
||||
echo "3. Configurar variables de entorno:"
|
||||
echo " - Copiar contenido de .env.production"
|
||||
echo " - Ajustar LIVEKIT_URL y secrets"
|
||||
echo ""
|
||||
echo "4. Configurar build:"
|
||||
echo " - Dockerfile: ./Dockerfile"
|
||||
echo " - Puerto: 80 (nginx) o 6080 (directo)"
|
||||
echo ""
|
||||
echo "5. Configurar dominio y SSL en EasyPanel"
|
||||
echo ""
|
||||
echo "📁 ARCHIVOS LISTOS:"
|
||||
echo " ✅ Dockerfile (multi-stage optimizado)"
|
||||
echo " ✅ docker-compose.yml (con nginx proxy)"
|
||||
echo " ✅ nginx.conf (configuración completa)"
|
||||
echo " ✅ .env.production (variables de ejemplo)"
|
||||
echo ""
|
||||
echo "🔗 URLs después del deploy:"
|
||||
echo " • Admin: https://tu-dominio.com"
|
||||
echo " • API: https://tu-dominio.com/api/"
|
||||
echo " • Health: https://tu-dominio.com/nginx-health"
|
||||
echo ""
|
||||
echo "👤 Login por defecto:"
|
||||
echo " • Usuario: admin"
|
||||
echo " • Contraseña: [configurar en ADMIN_PASSWORD]"
|
||||
@ -1,221 +0,0 @@
|
||||
#!/bin/bash
|
||||
# Script para desplegar LiveKit self-hosted con exposición pública
|
||||
|
||||
set -e
|
||||
|
||||
# Colores
|
||||
RED='\033[0;31m'
|
||||
GREEN='\033[0;32m'
|
||||
YELLOW='\033[1;33m'
|
||||
BLUE='\033[0;34m'
|
||||
NC='\033[0m'
|
||||
|
||||
echo -e "${BLUE}🏠 Configurando LiveKit Self-Hosted con exposición pública${NC}"
|
||||
echo ""
|
||||
|
||||
# Detectar IP local
|
||||
LOCAL_IP=$(hostname -I | awk '{print $1}')
|
||||
echo -e "${BLUE}🌐 IP Local detectada: $LOCAL_IP${NC}"
|
||||
|
||||
# Preguntar dominio/IP pública
|
||||
echo "¿Cuál es tu configuración de acceso público?"
|
||||
echo "1) Tengo IP pública fija"
|
||||
echo "2) IP dinámica - usar DuckDNS"
|
||||
echo "3) Solo testing local"
|
||||
echo ""
|
||||
read -p "Selecciona opción (1-3): " IP_OPTION
|
||||
|
||||
case $IP_OPTION in
|
||||
1)
|
||||
read -p "Ingresa tu IP pública: " PUBLIC_IP
|
||||
EXTERNAL_HOST="$PUBLIC_IP"
|
||||
;;
|
||||
2)
|
||||
read -p "Ingresa tu subdominio DuckDNS (ej: mi-livekit): " DUCKDNS_SUBDOMAIN
|
||||
EXTERNAL_HOST="$DUCKDNS_SUBDOMAIN.duckdns.org"
|
||||
echo -e "${YELLOW}📝 Recuerda configurar DuckDNS token después${NC}"
|
||||
;;
|
||||
3)
|
||||
EXTERNAL_HOST="$LOCAL_IP"
|
||||
echo -e "${YELLOW}⚠️ Solo funcionará en red local${NC}"
|
||||
;;
|
||||
*)
|
||||
echo -e "${RED}❌ Opción inválida${NC}"
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
|
||||
echo -e "${GREEN}🌐 Host externo configurado: $EXTERNAL_HOST${NC}"
|
||||
|
||||
# Generar secretos seguros
|
||||
API_SECRET=$(openssl rand -hex 32)
|
||||
REDIS_PASSWORD=$(openssl rand -hex 16)
|
||||
|
||||
echo -e "${YELLOW}🔧 Generando configuración...${NC}"
|
||||
|
||||
# Crear directorio SSL
|
||||
mkdir -p ssl logs
|
||||
|
||||
# Generar livekit-production.yaml
|
||||
cat > livekit-production.yaml << EOF
|
||||
port: 7880
|
||||
bind_addresses: ["0.0.0.0"]
|
||||
|
||||
# API Keys seguros (generados automáticamente)
|
||||
keys:
|
||||
production-key: $API_SECRET
|
||||
|
||||
# Redis para persistence y scaling
|
||||
redis:
|
||||
address: "redis:6379"
|
||||
password: "$REDIS_PASSWORD"
|
||||
db: 0
|
||||
|
||||
# RTC Configuration para acceso público
|
||||
rtc:
|
||||
# Rango de puertos UDP reducido pero suficiente
|
||||
port_range_start: 50000
|
||||
port_range_end: 50100
|
||||
|
||||
# Host/IP externa para acceso público
|
||||
use_external_ip: true
|
||||
external_ip: "$EXTERNAL_HOST"
|
||||
|
||||
# STUN servers para NAT traversal
|
||||
ice_servers:
|
||||
- urls: ["stun:stun.l.google.com:19302"]
|
||||
- urls: ["stun:stun1.l.google.com:19302"]
|
||||
|
||||
# Room settings para producción
|
||||
room:
|
||||
auto_create: true
|
||||
max_participants: 25
|
||||
empty_timeout: 600
|
||||
|
||||
# Logging para producción
|
||||
log_level: info
|
||||
log_format: json
|
||||
EOF
|
||||
|
||||
# Crear docker-compose-livekit-server.yml
|
||||
cat > docker-compose-livekit-server.yml << EOF
|
||||
version: '3.8'
|
||||
|
||||
services:
|
||||
livekit-server:
|
||||
image: livekit/livekit-server:latest
|
||||
container_name: livekit-production
|
||||
restart: unless-stopped
|
||||
ports:
|
||||
- "7880:7880"
|
||||
- "50000-50100:50000-50100/udp"
|
||||
volumes:
|
||||
- ./livekit-production.yaml:/livekit.yaml:ro
|
||||
- ./logs:/app/logs
|
||||
command: --config /livekit.yaml
|
||||
networks:
|
||||
- livekit-network
|
||||
depends_on:
|
||||
- redis
|
||||
|
||||
redis:
|
||||
image: redis:7-alpine
|
||||
container_name: livekit-redis
|
||||
restart: unless-stopped
|
||||
ports:
|
||||
- "6379:6379"
|
||||
command: redis-server --requirepass $REDIS_PASSWORD
|
||||
volumes:
|
||||
- redis_data:/data
|
||||
networks:
|
||||
- livekit-network
|
||||
|
||||
volumes:
|
||||
redis_data:
|
||||
|
||||
networks:
|
||||
livekit-network:
|
||||
driver: bridge
|
||||
EOF
|
||||
|
||||
# Crear variables para OpenVidu Meet
|
||||
cat > .env.livekit-client << EOF
|
||||
# Variables para EasyPanel/OpenVidu Meet
|
||||
LIVEKIT_URL=ws://$EXTERNAL_HOST:7880
|
||||
LIVEKIT_API_KEY=production-key
|
||||
LIVEKIT_API_SECRET=$API_SECRET
|
||||
EOF
|
||||
|
||||
echo -e "${GREEN}✅ Configuración generada${NC}"
|
||||
|
||||
# Configurar firewall
|
||||
echo -e "${YELLOW}🔥 Configurando firewall...${NC}"
|
||||
if command -v ufw &> /dev/null; then
|
||||
sudo ufw allow 7880/tcp comment "LiveKit API"
|
||||
sudo ufw allow 50000:50100/udp comment "LiveKit WebRTC"
|
||||
echo -e "${GREEN}✅ Firewall configurado${NC}"
|
||||
fi
|
||||
|
||||
# Parar servicios existentes
|
||||
echo -e "${YELLOW}🛑 Parando servicios existentes...${NC}"
|
||||
docker-compose -f docker-compose-livekit-server.yml down 2>/dev/null || true
|
||||
|
||||
# Iniciar LiveKit Server
|
||||
echo -e "${YELLOW}🚀 Iniciando LiveKit Server...${NC}"
|
||||
docker-compose -f docker-compose-livekit-server.yml up -d
|
||||
|
||||
# Esperar inicio
|
||||
echo -e "${YELLOW}⏳ Esperando que LiveKit inicie...${NC}"
|
||||
sleep 15
|
||||
|
||||
# Verificar servicios
|
||||
echo -e "${BLUE}🔍 Verificando servicios...${NC}"
|
||||
|
||||
if curl -s http://localhost:7880 > /dev/null 2>&1; then
|
||||
echo -e "${GREEN}✅ LiveKit API funcionando${NC}"
|
||||
else
|
||||
echo -e "${RED}❌ LiveKit no responde${NC}"
|
||||
fi
|
||||
|
||||
if docker exec livekit-redis redis-cli -a $REDIS_PASSWORD ping > /dev/null 2>&1; then
|
||||
echo -e "${GREEN}✅ Redis funcionando${NC}"
|
||||
else
|
||||
echo -e "${RED}❌ Redis no responde${NC}"
|
||||
fi
|
||||
|
||||
# Mostrar configuración final
|
||||
echo -e "${GREEN}"
|
||||
echo "============================================="
|
||||
echo "🎉 LIVEKIT SELF-HOSTED CONFIGURADO"
|
||||
echo "============================================="
|
||||
echo "🌐 Host externo: $EXTERNAL_HOST"
|
||||
echo "🔌 Puerto API: 7880"
|
||||
echo "📡 Puertos UDP: 50000-50100"
|
||||
echo ""
|
||||
echo "📋 CONFIGURACIÓN PARA OPENVIDU MEET:"
|
||||
echo " LIVEKIT_URL=ws://$EXTERNAL_HOST:7880"
|
||||
echo " LIVEKIT_API_KEY=production-key"
|
||||
echo " LIVEKIT_API_SECRET=$API_SECRET"
|
||||
echo ""
|
||||
echo "🔧 PASOS SIGUIENTES:"
|
||||
echo "1. Configurar port forwarding en router:"
|
||||
echo " - TCP 7880 → $LOCAL_IP:7880"
|
||||
echo " - UDP 50000-50100 → $LOCAL_IP:50000-50100"
|
||||
echo ""
|
||||
if [[ $IP_OPTION == 2 ]]; then
|
||||
echo "2. Configurar DuckDNS:"
|
||||
echo " - Token en duckdns.org"
|
||||
echo " - Script de actualización automática"
|
||||
echo ""
|
||||
fi
|
||||
echo "3. Configurar OpenVidu Meet con variables generadas"
|
||||
echo "4. (Opcional) Configurar SSL/HTTPS con Let's Encrypt"
|
||||
echo "============================================="
|
||||
echo -e "${NC}"
|
||||
|
||||
# Mostrar logs
|
||||
read -p "¿Ver logs de LiveKit en tiempo real? (y/N): " -n 1 -r
|
||||
echo
|
||||
if [[ $REPLY =~ ^[Yy]$ ]]; then
|
||||
docker-compose -f docker-compose-livekit-server.yml logs -f livekit-server
|
||||
fi
|
||||
@ -1,125 +0,0 @@
|
||||
#!/bin/bash
|
||||
# Script completo para desplegar OpenVidu Meet con LiveKit local y UDP
|
||||
|
||||
set -e
|
||||
|
||||
echo "🚀 Desplegando OpenVidu Meet con LiveKit local (UDP)..."
|
||||
|
||||
# Colores para output
|
||||
RED='\033[0;31m'
|
||||
GREEN='\033[0;32m'
|
||||
YELLOW='\033[1;33m'
|
||||
BLUE='\033[0;34m'
|
||||
NC='\033[0m' # No Color
|
||||
|
||||
# Verificar Docker
|
||||
if ! command -v docker &> /dev/null; then
|
||||
echo -e "${RED}❌ Docker no está instalado${NC}"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if ! command -v docker-compose &> /dev/null; then
|
||||
echo -e "${RED}❌ Docker Compose no está instalado${NC}"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Obtener IP local
|
||||
LOCAL_IP=$(hostname -I | awk '{print $1}')
|
||||
echo -e "${BLUE}🌐 IP Local detectada: $LOCAL_IP${NC}"
|
||||
|
||||
# Crear .env para local
|
||||
echo -e "${YELLOW}📝 Creando configuración local...${NC}"
|
||||
cat > .env.local << EOF
|
||||
# Configuración LOCAL con LiveKit y UDP
|
||||
ADMIN_PASSWORD=admin123
|
||||
REDIS_PASSWORD=redispassword
|
||||
|
||||
# LiveKit Local
|
||||
LIVEKIT_URL=ws://$LOCAL_IP:7880
|
||||
LIVEKIT_API_KEY=devkey
|
||||
LIVEKIT_API_SECRET=secretsecretsecretsecretsecretsecret
|
||||
|
||||
# Redis Local
|
||||
REDIS_HOST=$LOCAL_IP
|
||||
REDIS_PORT=6379
|
||||
EOF
|
||||
|
||||
# Actualizar IP en livekit-local.yaml
|
||||
echo -e "${YELLOW}🔧 Configurando LiveKit para IP $LOCAL_IP...${NC}"
|
||||
sed -i "s/external_ip: \".*\"/external_ip: \"$LOCAL_IP\"/" livekit-local.yaml
|
||||
|
||||
# Configurar firewall
|
||||
echo -e "${YELLOW}🔥 Configurando firewall...${NC}"
|
||||
./configure-udp-ports.sh
|
||||
|
||||
# Parar servicios existentes
|
||||
echo -e "${YELLOW}🛑 Parando servicios existentes...${NC}"
|
||||
docker-compose -f docker-compose-with-livekit.yml down 2>/dev/null || true
|
||||
|
||||
# Construir imágenes
|
||||
echo -e "${YELLOW}🔨 Construyendo imágenes...${NC}"
|
||||
docker-compose -f docker-compose-with-livekit.yml build
|
||||
|
||||
# Iniciar servicios
|
||||
echo -e "${YELLOW}🚀 Iniciando servicios completos...${NC}"
|
||||
docker-compose -f docker-compose-with-livekit.yml --env-file .env.local up -d
|
||||
|
||||
# Esperar a que los servicios estén listos
|
||||
echo -e "${YELLOW}⏳ Esperando servicios...${NC}"
|
||||
sleep 15
|
||||
|
||||
# Verificar servicios
|
||||
echo -e "${BLUE}🔍 Verificando servicios...${NC}"
|
||||
|
||||
services=(
|
||||
"redis:6379"
|
||||
"livekit:7880"
|
||||
"openvidu-meet:6080"
|
||||
"nginx:80"
|
||||
)
|
||||
|
||||
for service in "${services[@]}"; do
|
||||
name=$(echo $service | cut -d: -f1)
|
||||
port=$(echo $service | cut -d: -f2)
|
||||
|
||||
if curl -s http://localhost:$port > /dev/null 2>&1; then
|
||||
echo -e "${GREEN}✅ $name funcionando en puerto $port${NC}"
|
||||
else
|
||||
echo -e "${RED}❌ $name no responde en puerto $port${NC}"
|
||||
fi
|
||||
done
|
||||
|
||||
# Verificar puertos UDP
|
||||
echo -e "${BLUE}📊 Verificando puertos UDP...${NC}"
|
||||
if ss -tulnp | grep -q ":50000-60000"; then
|
||||
echo -e "${GREEN}✅ Puertos UDP 50000-60000 abiertos${NC}"
|
||||
else
|
||||
echo -e "${YELLOW}⚠️ No se detectan puertos UDP - verificar manualmente${NC}"
|
||||
fi
|
||||
|
||||
# Mostrar URLs finales
|
||||
echo -e "${GREEN}"
|
||||
echo "============================================="
|
||||
echo "🎉 DESPLIEGUE COMPLETADO"
|
||||
echo "============================================="
|
||||
echo "📱 OpenVidu Meet: http://$LOCAL_IP"
|
||||
echo "👨💼 Admin Panel: http://$LOCAL_IP/admin"
|
||||
echo "🔧 LiveKit API: http://$LOCAL_IP:7880"
|
||||
echo "📊 Redis: $LOCAL_IP:6379"
|
||||
echo ""
|
||||
echo "🔐 Credenciales Admin:"
|
||||
echo " Usuario: admin"
|
||||
echo " Password: admin123"
|
||||
echo ""
|
||||
echo "⚠️ PUERTOS NECESARIOS:"
|
||||
echo " TCP: 80, 6080, 6379, 7880"
|
||||
echo " UDP: 50000-60000 (WebRTC)"
|
||||
echo "============================================="
|
||||
echo -e "${NC}"
|
||||
|
||||
# Mostrar logs en tiempo real (opcional)
|
||||
read -p "¿Ver logs en tiempo real? (y/N): " -n 1 -r
|
||||
echo
|
||||
if [[ $REPLY =~ ^[Yy]$ ]]; then
|
||||
docker-compose -f docker-compose-with-livekit.yml --env-file .env.local logs -f
|
||||
fi
|
||||
@ -1,94 +0,0 @@
|
||||
version: '3.8'
|
||||
|
||||
services:
|
||||
# OpenVidu Meet Backend
|
||||
openvidu-meet:
|
||||
build: .
|
||||
container_name: openvidu-meet
|
||||
restart: unless-stopped
|
||||
environment:
|
||||
NODE_ENV: production
|
||||
MEET_LOG_LEVEL: info
|
||||
MEET_BLOB_STORAGE_MODE: memory
|
||||
PORT: 6080
|
||||
|
||||
# Admin user
|
||||
MEET_INITIAL_ADMIN_USER: admin
|
||||
MEET_INITIAL_ADMIN_PASSWORD: ${ADMIN_PASSWORD:-admin123}
|
||||
|
||||
# CORS y proxy
|
||||
SERVER_CORS_ORIGIN: "*"
|
||||
TRUST_PROXY: "true"
|
||||
|
||||
# LiveKit LOCAL con UDP
|
||||
LIVEKIT_URL: ${LIVEKIT_URL:-ws://192.168.1.19:7880}
|
||||
LIVEKIT_API_KEY: ${LIVEKIT_API_KEY:-devkey}
|
||||
LIVEKIT_API_SECRET: ${LIVEKIT_API_SECRET:-secretsecretsecretsecretsecretsecret}
|
||||
|
||||
ports:
|
||||
- "6080:6080"
|
||||
volumes:
|
||||
- ./logs:/app/logs
|
||||
healthcheck:
|
||||
test: ["CMD", "wget", "--quiet", "--tries=1", "--spider", "http://localhost:6080/"]
|
||||
interval: 30s
|
||||
timeout: 10s
|
||||
retries: 3
|
||||
start_period: 60s
|
||||
networks:
|
||||
- openvidu-network
|
||||
|
||||
# LiveKit Server LOCAL con puertos UDP
|
||||
livekit:
|
||||
image: livekit/livekit-server:latest
|
||||
container_name: openvidu-livekit
|
||||
restart: unless-stopped
|
||||
ports:
|
||||
# Puerto API/WebSocket
|
||||
- "7880:7880"
|
||||
# Rango UDP para WebRTC (IMPORTANTE!)
|
||||
- "50000-60000:50000-60000/udp"
|
||||
volumes:
|
||||
- ./livekit.yaml:/livekit.yaml:ro
|
||||
command: --config /livekit.yaml
|
||||
networks:
|
||||
- openvidu-network
|
||||
depends_on:
|
||||
- redis
|
||||
|
||||
# Redis para LiveKit
|
||||
redis:
|
||||
image: redis:7-alpine
|
||||
container_name: openvidu-redis
|
||||
restart: unless-stopped
|
||||
ports:
|
||||
- "6379:6379"
|
||||
command: redis-server --requirepass ${REDIS_PASSWORD:-redispassword}
|
||||
volumes:
|
||||
- redis_data:/data
|
||||
networks:
|
||||
- openvidu-network
|
||||
|
||||
# Nginx Proxy
|
||||
nginx-proxy:
|
||||
image: nginx:alpine
|
||||
container_name: openvidu-nginx
|
||||
restart: unless-stopped
|
||||
ports:
|
||||
- "80:80"
|
||||
volumes:
|
||||
- ./nginx.conf:/etc/nginx/nginx.conf:ro
|
||||
- nginx-cache:/var/cache/nginx
|
||||
depends_on:
|
||||
- openvidu-meet
|
||||
- livekit
|
||||
networks:
|
||||
- openvidu-network
|
||||
|
||||
volumes:
|
||||
nginx-cache:
|
||||
redis_data:
|
||||
|
||||
networks:
|
||||
openvidu-network:
|
||||
driver: bridge
|
||||
@ -1,70 +0,0 @@
|
||||
version: '3.8'
|
||||
|
||||
services:
|
||||
# OpenVidu Meet Backend
|
||||
openvidu-meet:
|
||||
build: .
|
||||
container_name: openvidu-meet-ce
|
||||
restart: unless-stopped
|
||||
environment:
|
||||
# Configuración básica
|
||||
NODE_ENV: production
|
||||
MEET_LOG_LEVEL: info
|
||||
MEET_BLOB_STORAGE_MODE: memory
|
||||
PORT: 6080
|
||||
|
||||
# Admin user (cambiar en producción)
|
||||
MEET_INITIAL_ADMIN_USER: admin
|
||||
MEET_INITIAL_ADMIN_PASSWORD: ${ADMIN_PASSWORD:-admin123}
|
||||
|
||||
# CORS para proxy
|
||||
SERVER_CORS_ORIGIN: "*"
|
||||
|
||||
# Configuración para proxy
|
||||
TRUST_PROXY: "true"
|
||||
|
||||
# LiveKit (ajustar según tu setup)
|
||||
LIVEKIT_URL: ${LIVEKIT_URL:-ws://localhost:7880}
|
||||
LIVEKIT_API_KEY: ${LIVEKIT_API_KEY:-devkey}
|
||||
LIVEKIT_API_SECRET: ${LIVEKIT_API_SECRET:-your-secret-key-32-chars-long}
|
||||
|
||||
# Redis (opcional - si no se proporciona, usa memoria)
|
||||
MEET_REDIS_HOST: ${REDIS_HOST:-}
|
||||
MEET_REDIS_PORT: ${REDIS_PORT:-6379}
|
||||
MEET_REDIS_PASSWORD: ${REDIS_PASSWORD:-}
|
||||
|
||||
ports:
|
||||
- "6080:6080"
|
||||
volumes:
|
||||
# Logs persistentes
|
||||
- ./logs:/app/logs
|
||||
healthcheck:
|
||||
test: ["CMD", "wget", "--quiet", "--tries=1", "--spider", "http://localhost:6080/"]
|
||||
interval: 30s
|
||||
timeout: 10s
|
||||
retries: 3
|
||||
start_period: 60s
|
||||
networks:
|
||||
- openvidu-network
|
||||
|
||||
# Nginx Proxy - Solo puerto 80 para EasyPanel
|
||||
nginx-proxy:
|
||||
image: nginx:alpine
|
||||
container_name: openvidu-nginx
|
||||
restart: unless-stopped
|
||||
ports:
|
||||
- "80:80" # Solo HTTP - EasyPanel maneja SSL con Traefik
|
||||
volumes:
|
||||
- ./nginx.conf:/etc/nginx/nginx.conf:ro
|
||||
- nginx-cache:/var/cache/nginx
|
||||
depends_on:
|
||||
- openvidu-meet
|
||||
networks:
|
||||
- openvidu-network
|
||||
|
||||
volumes:
|
||||
nginx-cache:
|
||||
|
||||
networks:
|
||||
openvidu-network:
|
||||
driver: bridge
|
||||
@ -1,178 +0,0 @@
|
||||
|
||||
# Javascript snippets
|
||||
|
||||
- `np` - nextPage
|
||||
- `npssp` - nextPageServerSideProps
|
||||
- `npsp` - nextPageStaticProps
|
||||
- `npspth` - nextPageStaticPaths
|
||||
- `nssp` - nextServerSideProps
|
||||
- `nsp` - nextStaticProps
|
||||
- `nspth` - nextStaticPaths
|
||||
- `nip` - nextInitialProps
|
||||
- `nimg` - nextImage
|
||||
- `napp` - nextApp
|
||||
- `ndoc` - nextDocument
|
||||
- `napi` - nextApi
|
||||
- `nmid` - nextMiddleware
|
||||
|
||||
## `np` - nextPage
|
||||
|
||||
```javascript
|
||||
const FileName = ({}) => {
|
||||
return <div></div>
|
||||
}
|
||||
|
||||
export default FileName
|
||||
```
|
||||
|
||||
## `npssp` - nextPageServerSideProps
|
||||
|
||||
```javascript
|
||||
const FileName = ({}) => {
|
||||
return <div></div>
|
||||
}
|
||||
|
||||
export const getServerSideProps = async (ctx) => {
|
||||
return {
|
||||
props: {}
|
||||
}
|
||||
}
|
||||
|
||||
export default FileName
|
||||
```
|
||||
|
||||
## `npsp` - nextPageStaticProps
|
||||
|
||||
```javascript
|
||||
const FileName = ({}) => {
|
||||
return <div></div>
|
||||
}
|
||||
|
||||
export const getStaticProps = async (ctx) => {
|
||||
return {
|
||||
props: {},
|
||||
}
|
||||
}
|
||||
|
||||
export default FileName
|
||||
```
|
||||
|
||||
## `npspth` - nextPageStaticPaths
|
||||
|
||||
```javascript
|
||||
const FileName = ({}) => {
|
||||
return <div></div>
|
||||
}
|
||||
|
||||
export const getStaticPaths = async () => {
|
||||
return {
|
||||
paths: [],
|
||||
fallback: false,
|
||||
}
|
||||
}
|
||||
|
||||
export default FileName
|
||||
```
|
||||
|
||||
## `nssp` - nextServerSideProps
|
||||
|
||||
```javascript
|
||||
export const getServerSideProps = async (ctx) => {
|
||||
return {
|
||||
props: {}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## `nsp` - nextStaticProps
|
||||
|
||||
```javascript
|
||||
export const getStaticProps = async (ctx) => {
|
||||
return {
|
||||
props: {},
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## `nspth` - nextStaticPaths
|
||||
|
||||
```javascript
|
||||
export const getStaticPaths = async () => {
|
||||
return {
|
||||
paths: [],
|
||||
fallback: false,
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## `nip` - nextInitialProps
|
||||
|
||||
```javascript
|
||||
FileName.getInitialProps = async (ctx) => {
|
||||
return {
|
||||
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## `nimg` - nextImage
|
||||
|
||||
```javascript
|
||||
<Image src="" alt="" />
|
||||
```
|
||||
|
||||
## `napp` - nextApp
|
||||
|
||||
```javascript
|
||||
export default function MyApp({ Component, pageProps }) {
|
||||
return <Component {...pageProps} />
|
||||
}
|
||||
```
|
||||
|
||||
## `ndoc` - nextDocument
|
||||
|
||||
```javascript
|
||||
import Document, { Html, Head, Main, NextScript } from 'next/document'
|
||||
|
||||
class MyDocument extends Document {
|
||||
static async getInitialProps(ctx) {
|
||||
const initialProps = await Document.getInitialProps(ctx)
|
||||
return { ...initialProps }
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<Html>
|
||||
<Head />
|
||||
<body>
|
||||
<Main />
|
||||
<NextScript />
|
||||
</body>
|
||||
</Html>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default MyDocument
|
||||
```
|
||||
|
||||
## `napi` - nextApi
|
||||
|
||||
```javascript
|
||||
export default async function handler(req, res) {
|
||||
|
||||
}
|
||||
```
|
||||
|
||||
## `nmid` - nextMiddleware
|
||||
|
||||
```javascript
|
||||
import { NextResponse } from 'next/server'
|
||||
export async function middleware(request) {
|
||||
|
||||
}
|
||||
|
||||
export const config = {
|
||||
matcher: '/about/:path*',
|
||||
}
|
||||
```
|
||||
@ -1,202 +0,0 @@
|
||||
|
||||
# Typescript snippets
|
||||
|
||||
- `np` - nextPage
|
||||
- `npssp` - nextPageServerSideProps
|
||||
- `npsp` - nextPageStaticProps
|
||||
- `npspth` - nextPageStaticPaths
|
||||
- `nssp` - nextServerSideProps
|
||||
- `nsp` - nextStaticProps
|
||||
- `nspth` - nextStaticPaths
|
||||
- `nip` - nextInitialProps
|
||||
- `nimg` - nextImage
|
||||
- `napp` - nextApp
|
||||
- `ndoc` - nextDocument
|
||||
- `napi` - nextApi
|
||||
- `nmid` - nextMiddleware
|
||||
|
||||
## `np` - nextPage
|
||||
|
||||
```typescript
|
||||
import { NextPage } from 'next'
|
||||
|
||||
interface Props {}
|
||||
|
||||
const FileName: NextPage<Props> = ({}) => {
|
||||
return <div></div>
|
||||
}
|
||||
|
||||
export default FileName
|
||||
```
|
||||
|
||||
## `npssp` - nextPageServerSideProps
|
||||
|
||||
```typescript
|
||||
import { NextPage, GetServerSideProps } from 'next'
|
||||
|
||||
interface Props {}
|
||||
|
||||
const FileName: NextPage<Props> = ({}) => {
|
||||
return <div></div>
|
||||
}
|
||||
|
||||
export const getServerSideProps: GetServerSideProps = async (ctx) => {
|
||||
return {
|
||||
props: {}
|
||||
}
|
||||
}
|
||||
|
||||
export default FileName
|
||||
```
|
||||
|
||||
## `npsp` - nextPageStaticProps
|
||||
|
||||
```typescript
|
||||
import { NextPage, GetStaticProps } from 'next'
|
||||
|
||||
interface Props {}
|
||||
|
||||
const FileName: NextPage<Props> = ({}) => {
|
||||
return <div></div>
|
||||
}
|
||||
|
||||
export const getStaticProps: GetStaticProps = async (ctx) => {
|
||||
return {
|
||||
props: {},
|
||||
}
|
||||
}
|
||||
|
||||
export default FileName
|
||||
```
|
||||
|
||||
## `npspth` - nextPageStaticPaths
|
||||
|
||||
```typescript
|
||||
import { NextPage, GetStaticPaths } from 'next'
|
||||
|
||||
interface Props {}
|
||||
|
||||
const FileName: NextPage<Props> = ({}) => {
|
||||
return <div></div>
|
||||
}
|
||||
|
||||
export const getStaticPaths: GetStaticPaths = async () => {
|
||||
return {
|
||||
paths: [],
|
||||
fallback: false,
|
||||
}
|
||||
}
|
||||
|
||||
export default FileName
|
||||
```
|
||||
|
||||
## `nssp` - nextServerSideProps
|
||||
|
||||
```typescript
|
||||
export const getServerSideProps: GetServerSideProps = async (ctx) => {
|
||||
return {
|
||||
props: {}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## `nsp` - nextStaticProps
|
||||
|
||||
```typescript
|
||||
export const getStaticProps: GetStaticProps = async (ctx) => {
|
||||
return {
|
||||
props: {},
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## `nspth` - nextStaticPaths
|
||||
|
||||
```typescript
|
||||
export const getStaticPaths: GetStaticPaths = async () => {
|
||||
return {
|
||||
paths: [],
|
||||
fallback: false,
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## `nip` - nextInitialProps
|
||||
|
||||
```typescript
|
||||
FileName.getInitialProps = async (ctx) => {
|
||||
return {
|
||||
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## `nimg` - nextImage
|
||||
|
||||
```typescript
|
||||
<Image src="" alt="" />
|
||||
```
|
||||
|
||||
## `napp` - nextApp
|
||||
|
||||
```typescript
|
||||
import type { AppProps } from 'next/app'
|
||||
|
||||
export default function MyApp({ Component, pageProps }: AppProps) {
|
||||
return <Component {...pageProps} />
|
||||
}
|
||||
```
|
||||
|
||||
## `ndoc` - nextDocument
|
||||
|
||||
```typescript
|
||||
import Document, { Html, Head, Main, NextScript, DocumentContext } from 'next/document'
|
||||
|
||||
class MyDocument extends Document {
|
||||
static async getInitialProps(ctx: DocumentContext) {
|
||||
const initialProps = await Document.getInitialProps(ctx)
|
||||
return { ...initialProps }
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<Html>
|
||||
<Head />
|
||||
<body>
|
||||
<Main />
|
||||
<NextScript />
|
||||
</body>
|
||||
</Html>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default MyDocument
|
||||
```
|
||||
|
||||
## `napi` - nextApi
|
||||
|
||||
```typescript
|
||||
import type { NextApiRequest, NextApiResponse } from 'next'
|
||||
|
||||
interface Data {}
|
||||
|
||||
export default async function handler(req: NextApiRequest, res: NextApiResponse<Data>) {
|
||||
|
||||
}
|
||||
```
|
||||
|
||||
## `nmid` - nextMiddleware
|
||||
|
||||
```typescript
|
||||
import { NextResponse } from 'next/server'
|
||||
import type { NextRequest } from 'next/server'
|
||||
|
||||
export async function middleware(request: NextRequest) {
|
||||
|
||||
}
|
||||
|
||||
export const config = {
|
||||
matcher: '/about/:path*',
|
||||
}
|
||||
```
|
||||
@ -1,68 +0,0 @@
|
||||
#!/bin/bash
|
||||
set -e
|
||||
|
||||
echo "🔧 SOLUCIONANDO PROBLEMA DE LOGIN ADMIN"
|
||||
echo "======================================"
|
||||
|
||||
echo "1. Parando backend actual..."
|
||||
pkill -f "node.*dist/src/server.js" 2>/dev/null || true
|
||||
sleep 3
|
||||
|
||||
echo "2. Configurando backend con storage en memoria..."
|
||||
cd /home/xesar/Documentos/openvidu-meet/meet-ce/backend
|
||||
|
||||
# Crear backup del log anterior
|
||||
[ -f /tmp/ovm-logs/backend.log ] && mv /tmp/ovm-logs/backend.log /tmp/ovm-logs/backend.log.backup
|
||||
|
||||
echo "3. Arrancando backend con configuración correcta..."
|
||||
nohup env \
|
||||
NODE_ENV=development \
|
||||
MEET_LOG_LEVEL=debug \
|
||||
MEET_BLOB_STORAGE_MODE=memory \
|
||||
MEET_INITIAL_ADMIN_USER=admin \
|
||||
MEET_INITIAL_ADMIN_PASSWORD=admin \
|
||||
LIVEKIT_URL=ws://192.168.1.19:7880 \
|
||||
LIVEKIT_URL_PRIVATE=ws://192.168.1.19:7880 \
|
||||
LIVEKIT_API_KEY=devkey \
|
||||
LIVEKIT_API_SECRET=secretsecretsecretsecretsecretsecret \
|
||||
MEET_REDIS_HOST=192.168.1.19 \
|
||||
MEET_REDIS_PORT=6379 \
|
||||
MEET_REDIS_PASSWORD=redispassword \
|
||||
node dist/src/server.js > /tmp/ovm-logs/backend.log 2>&1 &
|
||||
|
||||
BACKEND_PID=$!
|
||||
echo "✅ Backend iniciado con PID: $BACKEND_PID"
|
||||
|
||||
echo "4. Esperando arranque (10s)..."
|
||||
sleep 10
|
||||
|
||||
echo "5. Verificando estado:"
|
||||
if ps -p $BACKEND_PID >/dev/null 2>&1; then
|
||||
echo "✅ Proceso backend activo"
|
||||
else
|
||||
echo "❌ Proceso backend inactivo"
|
||||
echo "Logs de error:"
|
||||
tail -n 10 /tmp/ovm-logs/backend.log
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if ss -ltn | grep -q :6080; then
|
||||
echo "✅ Puerto 6080 activo"
|
||||
else
|
||||
echo "❌ Puerto 6080 inactivo"
|
||||
fi
|
||||
|
||||
echo "6. Verificando logs de admin:"
|
||||
grep -i "admin\|storage.*mode\|memory" /tmp/ovm-logs/backend.log | tail -5
|
||||
|
||||
echo ""
|
||||
echo "🎉 SOLUCION COMPLETADA"
|
||||
echo "======================"
|
||||
echo "✅ Backend corriendo con storage en memoria"
|
||||
echo "✅ Usuario admin configurado: admin/admin"
|
||||
echo "🌐 Accede a: http://192.168.1.19:6080"
|
||||
echo "📄 Logs en: /tmp/ovm-logs/backend.log"
|
||||
echo ""
|
||||
echo "👤 CREDENCIALES DE LOGIN:"
|
||||
echo " Usuario: admin"
|
||||
echo " Contraseña: admin"
|
||||
@ -1,46 +0,0 @@
|
||||
# LiveKit Server Configuration for LOCAL deployment with UDP
|
||||
# Para usar con Docker Compose completo incluyendo LiveKit
|
||||
|
||||
port: 7880
|
||||
bind_addresses: [""]
|
||||
|
||||
# API Keys (mismo secret que en backend)
|
||||
keys:
|
||||
devkey: secretsecretsecretsecretsecretsecret
|
||||
|
||||
# Redis para coordinación
|
||||
redis:
|
||||
address: redis:6379
|
||||
password: redispassword
|
||||
db: 0
|
||||
|
||||
# Configuración RTC con UDP para red local
|
||||
rtc:
|
||||
# Rango de puertos UDP (DEBE coincidir con docker-compose)
|
||||
port_range_start: 50000
|
||||
port_range_end: 60000
|
||||
|
||||
# IP externa para acceso desde otros dispositivos
|
||||
# Cambiar por tu IP local real
|
||||
use_external_ip: true
|
||||
external_ip: "192.168.1.19"
|
||||
|
||||
# Configuración ICE/STUN
|
||||
ice_servers:
|
||||
- urls: ["stun:stun.l.google.com:19302"]
|
||||
|
||||
# Configuración de rooms
|
||||
room:
|
||||
auto_create: true
|
||||
max_participants: 0
|
||||
empty_timeout: 300
|
||||
|
||||
# Egress para grabaciones (requiere Redis)
|
||||
# Habilitado automáticamente con Redis
|
||||
|
||||
# Logging
|
||||
log_level: info
|
||||
log_format: json
|
||||
|
||||
# Configuración de desarrollo
|
||||
development: true
|
||||
41
livekit.yaml
41
livekit.yaml
@ -1,41 +0,0 @@
|
||||
# LiveKit Server Configuration for Development
|
||||
# https://docs.livekit.io/deploy/configuration/
|
||||
|
||||
port: 7880
|
||||
# Admin/API port (HTTP for rooms/egress APIs)
|
||||
# The default admin port is 7880 for WebSocket and HTTP admin API
|
||||
# Some builds use separate ports; adjust if needed
|
||||
|
||||
# API Keys for authentication (secret must be 32+ characters)
|
||||
keys:
|
||||
devkey: secretsecretsecretsecretsecretsecret
|
||||
|
||||
# Redis configuration (required for egress, ingress, and multi-node deployments)
|
||||
redis:
|
||||
address: 192.168.1.20:6380
|
||||
password: 52a4a5b5efdd2ac4a8fd
|
||||
db: 0
|
||||
|
||||
# Enable egress service (recording/streaming)
|
||||
# Egress requires Redis to coordinate recording jobs
|
||||
# If you see "egress not connected (redis required)", ensure Redis config is correct above
|
||||
|
||||
# Development mode settings
|
||||
log_level: debug
|
||||
|
||||
# RTC configuration
|
||||
rtc:
|
||||
# Use ephemeral ports for UDP
|
||||
port_range_start: 50000
|
||||
port_range_end: 60000
|
||||
# Allow connection from network (not just localhost)
|
||||
use_external_ip: true
|
||||
|
||||
# Room settings
|
||||
room:
|
||||
# Auto-create rooms when participants join
|
||||
auto_create: true
|
||||
# Max participants per room (0 = unlimited)
|
||||
max_participants: 0
|
||||
# Empty room timeout (in seconds, 0 = no timeout)
|
||||
empty_timeout: 300
|
||||
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.nodeEnv": {
|
||||
"NODE_OPTIONS": "--experimental-vm-modules"
|
||||
},
|
||||
"jest.runMode": "on-demand"
|
||||
|
||||
}
|
||||
|
||||
@ -1,39 +1,4 @@
|
||||
USE_HTTPS=true
|
||||
USE_HTTPS=false
|
||||
MEET_LOG_LEVEL=debug
|
||||
SERVER_CORS_ORIGIN=*
|
||||
MEET_INITIAL_API_KEY=meet-api-key
|
||||
|
||||
# Admin user configuration (initial admin user created on first startup)
|
||||
MEET_INITIAL_ADMIN_USER=admin
|
||||
MEET_INITIAL_ADMIN_PASSWORD=admin
|
||||
|
||||
# Redis configuration (used by the backend). Defaults in code point to localhost:6379.
|
||||
# If you don't have a Redis server running locally, you can start one with Docker:
|
||||
# docker run --name openvidu-redis -p 6379:6379 -d redis:7
|
||||
# Or with podman:
|
||||
# podman run --name openvidu-redis -p 6379:6379 -d docker.io/library/redis:7
|
||||
# Environment variables read by the server (optional - only needed if you want to change defaults):
|
||||
MEET_REDIS_HOST=192.168.1.19
|
||||
MEET_REDIS_PORT=6379
|
||||
MEET_REDIS_PASSWORD=redispassword
|
||||
MEET_REDIS_DB=0
|
||||
|
||||
# If using Redis Sentinel, set the host list as comma separated host:port pairs and the sentinel password:
|
||||
# MEET_REDIS_SENTINEL_HOST_LIST=sentinel1:26379,sentinel2:26379
|
||||
# MEET_REDIS_SENTINEL_PASSWORD=your-sentinel-password
|
||||
# LiveKit URL — use the websocket URL that corresponds to the admin HTTP port.
|
||||
# The livekit-server process here is listening on 7880 (client) and 7881 (admin).
|
||||
# Point LIVEKIT_URL/LIVEKIT_URL_PRIVATE to the admin-enabled port so server-side
|
||||
# clients (egress/room service) use the correct HTTP admin endpoint.
|
||||
LIVEKIT_URL=ws://nextream.sytes.net:7880
|
||||
LIVEKIT_URL_PRIVATE=ws://nextream.sytes.net:7880
|
||||
LIVEKIT_API_KEY=devkey
|
||||
LIVEKIT_API_SECRET=secretsecretsecretsecretsecretsecret
|
||||
|
||||
# MinIO / S3 configuration for local development (temporarily using memory)
|
||||
MEET_BLOB_STORAGE_MODE=memory
|
||||
# MEET_S3_SERVICE_ENDPOINT=http://192.168.1.19:9000
|
||||
# MEET_S3_ACCESS_KEY=minioadmin
|
||||
# MEET_S3_SECRET_KEY=minioadmin
|
||||
# MEET_S3_BUCKET=openvidu-appdata
|
||||
# MEET_S3_WITH_PATH_STYLE_ACCESS=true
|
||||
MEET_INITIAL_API_KEY=meet-api-key
|
||||
@ -1,6 +1,7 @@
|
||||
USE_HTTPS=false
|
||||
MEET_LOG_LEVEL=verbose
|
||||
MEET_LOG_LEVEL=debug
|
||||
SERVER_CORS_ORIGIN=*
|
||||
MEET_INITIAL_API_KEY=meet-api-key
|
||||
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
|
||||
```
|
||||
|
||||
## 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
|
||||
|
||||
@ -109,6 +126,45 @@ Where:
|
||||
|
||||
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
|
||||
|
||||
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
|
||||
```
|
||||
|
||||
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.
|
||||
|
||||
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
|
||||
graph TD;
|
||||
@ -169,46 +225,53 @@ graph TD;
|
||||
L --> M
|
||||
M -->|More rooms| E
|
||||
M -->|No more rooms| N[Process completed]
|
||||
|
||||
```
|
||||
|
||||
5. **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).
|
||||
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 14 minutes to identify and abort recordings that haven't been updated within a configured threshold (5 minutes by default).
|
||||
|
||||
```mermaid
|
||||
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 -->|No recordings found| D[Log and exit]
|
||||
B -->|Recordings found| E[Process recordings in batches of 10]
|
||||
|
||||
E --> F[For each recording in batch]
|
||||
F --> G[Extract recording ID and updatedAt]
|
||||
G --> H[Get recording status from storage]
|
||||
F --> G[Extract recordingId, roomId and egressId]
|
||||
G --> H[Check for corresponding egress in LiveKit]
|
||||
|
||||
H -->|Recording already ABORTED| I[Mark as already processed]
|
||||
H -->|Recording active| J[Check if updatedAt exists]
|
||||
H -->|No egress found| I[Recording is stale - no egress exists]
|
||||
H -->|Egress exists| J[Extract updatedAt from egress]
|
||||
|
||||
J -->|No updatedAt timestamp| K[Keep as fresh - log warning]
|
||||
J -->|Has updatedAt| L[Calculate if stale]
|
||||
I --> K[Update status to ABORTED in database]
|
||||
K --> L[Log successful abort - no egress found]
|
||||
|
||||
L -->|Still fresh| M[Log as fresh]
|
||||
L -->|Is stale| N[Abort stale recording]
|
||||
J -->|No updatedAt timestamp| M[Keep as fresh - log warning]
|
||||
J -->|Has updatedAt| N[Check if recording age is stale]
|
||||
|
||||
N --> O[Update status to ABORTED in storage]
|
||||
N --> P[Stop egress in LiveKit]
|
||||
O --> Q[Log successful abort]
|
||||
P --> Q
|
||||
N -->|Age not stale| O[Log as fresh]
|
||||
N -->|Age is stale| P[Check room existence]
|
||||
|
||||
I --> R[Continue to next recording]
|
||||
K --> R
|
||||
M --> R
|
||||
Q --> R
|
||||
P -->|Room does not exist| Q[Mark as stale]
|
||||
P -->|Room exists| R[Check if room has participants]
|
||||
|
||||
R -->|More recordings in batch| F
|
||||
R -->|Batch complete| S[Process next batch]
|
||||
S -->|More batches| E
|
||||
S -->|All batches processed| T[Log completion metrics]
|
||||
T --> U[Process completed]
|
||||
R -->|No participants| Q
|
||||
R -->|Has participants| O
|
||||
|
||||
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';
|
||||
export * from './src/controllers/index.js';
|
||||
export * from './src/services/index.js';
|
||||
export * from './src/models/index.js';
|
||||
export * from './src/helpers/index.js';
|
||||
// Main entry point for @openvidu-meet/backend package
|
||||
export * from './src/config/internal-config.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/controllers/index.js';
|
||||
export * from './src/helpers/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';
|
||||
|
||||
|
||||
@ -15,16 +15,19 @@ const jestConfig = {
|
||||
'^(\\.{1,2}/.*)\\.js$': '$1' // Allow importing js files and resolving to ts files
|
||||
},
|
||||
transform: {
|
||||
'^.+\\.tsx?$': ['ts-jest', {
|
||||
tsconfig: {
|
||||
module: 'esnext',
|
||||
moduleResolution: 'node16',
|
||||
esModuleInterop: true,
|
||||
allowSyntheticDefaultImports: true,
|
||||
isolatedModules: true
|
||||
},
|
||||
useESM: true
|
||||
}]
|
||||
'^.+\\.tsx?$': [
|
||||
'ts-jest',
|
||||
{
|
||||
tsconfig: {
|
||||
module: 'esnext',
|
||||
moduleResolution: 'node16',
|
||||
esModuleInterop: true,
|
||||
allowSyntheticDefaultImports: 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,8 +0,0 @@
|
||||
env: ‘\t’: No existe el fichero o el directorio
|
||||
env: use -[v]S to pass options in shebang lines
|
||||
env: ‘\t’: No existe el fichero o el directorio
|
||||
env: use -[v]S to pass options in shebang lines
|
||||
env: ‘ ’: No existe el fichero o el directorio
|
||||
env: use -[v]S to pass options in shebang lines
|
||||
env: ‘\t’: No existe el fichero o el directorio
|
||||
env: use -[v]S to pass options in shebang lines
|
||||
@ -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
|
||||
in: path
|
||||
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:
|
||||
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
|
||||
in: query
|
||||
required: false
|
||||
description: |
|
||||
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.
|
||||
description: Filter recordings by their status.
|
||||
schema:
|
||||
type: string
|
||||
enum:
|
||||
|
||||
@ -2,7 +2,8 @@ name: roomName
|
||||
in: query
|
||||
required: false
|
||||
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:
|
||||
type: string
|
||||
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:
|
||||
enabled: true
|
||||
recording:
|
||||
enabled: false
|
||||
enabled: true
|
||||
encoding: H264_720P_30
|
||||
virtualBackground:
|
||||
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:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '../../schemas/error.yaml'
|
||||
$ref: '../schemas/error.yaml'
|
||||
examples:
|
||||
already_recording:
|
||||
summary: Room is already being recorded
|
||||
@ -2,7 +2,7 @@ description: Conflict — The recording is starting or already stopped
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '../../schemas/error.yaml'
|
||||
$ref: '../schemas/error.yaml'
|
||||
examples:
|
||||
starting_recording:
|
||||
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:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '../../schemas/error.yaml'
|
||||
$ref: '../schemas/error.yaml'
|
||||
examples:
|
||||
starting_timeout:
|
||||
summary: Recording service timed out
|
||||
@ -1,4 +1,4 @@
|
||||
description: Forbidden — Insufficient permissions
|
||||
description: Forbidden - Insufficient Permissions
|
||||
content:
|
||||
application/json:
|
||||
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:
|
||||
application/json:
|
||||
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:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '../../schemas/internal/meet-room-role-permissions.yaml'
|
||||
$ref: '../../schemas/internal/room-member-role-permissions.yaml'
|
||||
@ -4,7 +4,7 @@ content:
|
||||
schema:
|
||||
type: array
|
||||
items:
|
||||
$ref: '../../schemas/internal/meet-room-role-permissions.yaml'
|
||||
$ref: '../../schemas/internal/room-member-role-permissions.yaml'
|
||||
example:
|
||||
- role: 'moderator'
|
||||
permissions:
|
||||
@ -17,6 +17,8 @@ content:
|
||||
canUpdateOwnMetadata: true
|
||||
openvidu:
|
||||
canRecord: true
|
||||
canRetrieveRecordings: true
|
||||
canDeleteRecordings: true
|
||||
canChat: true
|
||||
canChangeVirtualBackground: true
|
||||
- role: 'speaker'
|
||||
@ -30,5 +32,7 @@ content:
|
||||
canUpdateOwnMetadata: true
|
||||
openvidu:
|
||||
canRecord: false
|
||||
canRetrieveRecordings: true
|
||||
canDeleteRecordings: false
|
||||
canChat: true
|
||||
canChangeVirtualBackground: true
|
||||
@ -6,4 +6,4 @@ content:
|
||||
properties:
|
||||
message:
|
||||
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
|
||||
# headers:
|
||||
# Set-Cookie:
|
||||
# $ref: '../../headers/set-cookie-access-token.yaml'
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
|
||||
@ -1,9 +1,4 @@
|
||||
description: Successfully logged in
|
||||
# headers:
|
||||
# Set-Cookie:
|
||||
# $ref: '../../headers/set-cookie-access-token.yaml'
|
||||
# Set-Cookie*:
|
||||
# $ref: '../../headers/set-cookie-refresh-token.yaml'
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
|
||||
@ -1,11 +1,4 @@
|
||||
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:
|
||||
application/json:
|
||||
schema:
|
||||
|
||||
@ -11,6 +11,7 @@ content:
|
||||
roomId: 'room-123'
|
||||
roomName: 'room'
|
||||
status: 'complete'
|
||||
layout: 'grid'
|
||||
filename: 'room-123--XX445.mp4'
|
||||
startDate: 1600000000000
|
||||
endDate: 1600000003600
|
||||
@ -25,5 +26,6 @@ content:
|
||||
roomId: 'room-456'
|
||||
roomName: 'room'
|
||||
status: 'active'
|
||||
layout: 'grid'
|
||||
filename: 'room-456--QR789.mp4'
|
||||
startDate: 1682500000000
|
||||
|
||||
@ -19,6 +19,7 @@ content:
|
||||
roomId: 'room-123'
|
||||
roomName: 'room'
|
||||
status: 'active'
|
||||
layout: 'grid'
|
||||
filename: 'room-123--XX445.mp4'
|
||||
startDate: 1620000000000
|
||||
endDate: 1620000003600
|
||||
@ -29,6 +30,7 @@ content:
|
||||
roomId: 'room-456'
|
||||
roomName: 'room'
|
||||
status: 'complete'
|
||||
layout: 'grid'
|
||||
filename: 'room-456--XX678.mp4'
|
||||
startDate: 1625000000000
|
||||
endDate: 1625000007200
|
||||
|
||||
@ -19,8 +19,15 @@ content:
|
||||
enabled: true
|
||||
recording:
|
||||
enabled: false
|
||||
layout: grid
|
||||
encoding: H264_720P_30
|
||||
allowAccessTo: admin_moderator_speaker
|
||||
virtualBackground:
|
||||
enabled: true
|
||||
e2ee:
|
||||
enabled: false
|
||||
captions:
|
||||
enabled: true
|
||||
moderatorUrl: 'http://localhost:6080/room/room-123?secret=123456'
|
||||
speakerUrl: 'http://localhost:6080/room/room-123?secret=654321'
|
||||
status: open
|
||||
@ -43,8 +50,21 @@ content:
|
||||
enabled: true
|
||||
recording:
|
||||
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:
|
||||
enabled: true
|
||||
e2ee:
|
||||
enabled: false
|
||||
|
||||
fields=moderatorUrl,speakerUrl:
|
||||
summary: Response containing only moderator and speaker URLs
|
||||
|
||||
@ -28,8 +28,15 @@ content:
|
||||
enabled: true
|
||||
recording:
|
||||
enabled: false
|
||||
layout: grid
|
||||
encoding: H264_720P_30
|
||||
allowAccessTo: admin_moderator_speaker
|
||||
virtualBackground:
|
||||
enabled: true
|
||||
e2ee:
|
||||
enabled: false
|
||||
captions:
|
||||
enabled: true
|
||||
moderatorUrl: 'http://localhost:6080/room/room-123?secret=123456'
|
||||
speakerUrl: 'http://localhost:6080/room/room-123?secret=654321'
|
||||
status: open
|
||||
@ -46,8 +53,21 @@ content:
|
||||
enabled: false
|
||||
recording:
|
||||
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:
|
||||
enabled: false
|
||||
e2ee:
|
||||
enabled: false
|
||||
moderatorUrl: 'http://localhost:6080/room/room-456?secret=789012'
|
||||
speakerUrl: 'http://localhost:6080/room/room-456?secret=210987'
|
||||
status: open
|
||||
@ -80,6 +100,8 @@ content:
|
||||
enabled: false
|
||||
virtualBackground:
|
||||
enabled: true
|
||||
e2ee:
|
||||
enabled: false
|
||||
- roomId: 'room-456'
|
||||
roomName: 'room'
|
||||
creationDate: 1620001000000
|
||||
@ -91,6 +113,8 @@ content:
|
||||
enabled: true
|
||||
virtualBackground:
|
||||
enabled: false
|
||||
e2ee:
|
||||
enabled: false
|
||||
pagination:
|
||||
isTruncated: true
|
||||
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:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '../../schemas/meet-recording.yaml'
|
||||
$ref: '../schemas/meet-recording.yaml'
|
||||
example:
|
||||
recordingId: 'room-123--EG_XYZ--XX445'
|
||||
roomId: 'room-123'
|
||||
roomName: 'room'
|
||||
status: 'active'
|
||||
layout: 'speaker'
|
||||
filename: 'room-123--XX445.mp4'
|
||||
startDate: 1600000000000
|
||||
headers:
|
||||
@ -8,12 +8,13 @@ headers:
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '../../schemas/meet-recording.yaml'
|
||||
$ref: '../schemas/meet-recording.yaml'
|
||||
example:
|
||||
recordingId: 'room-123--EG_XYZ--XX445'
|
||||
roomId: 'room-123'
|
||||
roomName: 'room'
|
||||
status: 'ending'
|
||||
layout: 'speaker'
|
||||
filename: 'room-123--XX445.mp4'
|
||||
startDate: 1600000000000
|
||||
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
|
||||
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