Compare commits

..

2 Commits

Author SHA1 Message Date
83af21cb81 fix: update Redis configuration in LiveKit and restart script
Some checks failed
Backend Unit Tests / Backend Unit Tests (push) Has been cancelled
WebComponent E2E Tests / WebComponent E2E Tests (push) Has been cancelled
WebComponent Unit Tests / WebComponent Unit Tests (push) Has been cancelled
2026-03-16 11:56:17 -07:00
64924f9999 Applied new local config 2025-11-01 00:49:33 -07:00
721 changed files with 21115 additions and 32440 deletions

View File

@ -141,6 +141,8 @@
**/*.mov
**/*.mkv
**/*.webm
**/*.mp3
**/*.wav
**/*.flac
# ====================================================

49
.env.production Normal file
View File

@ -0,0 +1,49 @@
# 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

View File

@ -2,14 +2,14 @@ name: Backend Integration Tests
on:
push:
paths:
- 'meet-ce/backend/src/**'
- 'meet-ce/backend/package.json'
- 'pnpm-lock.yaml'
- 'meet-ce/backend/tests/**'
- 'backend/src/**'
- 'backend/package.json'
- 'backend/pnpm-lock.yaml'
- 'backend/tests/**'
- '.github/workflows/backend-integration-test.yaml'
pull_request:
paths:
- 'meet-ce/backend/src/**'
- 'backend/src/**'
workflow_dispatch:
inputs:
use-aws:
@ -22,7 +22,7 @@ on:
jobs:
build-components:
name: Build OpenVidu Components Angular
runs-on: ${{ vars.LABEL_WORKER_SELFHOSTED }}
runs-on: ov-actions-runner
steps:
- name: Build Components
id: build
@ -33,19 +33,26 @@ jobs:
test-api:
name: ${{ matrix.test-name }}
needs: build-components
runs-on: ${{ vars.LABEL_WORKER_SELFHOSTED }}
runs-on: ov-actions-runner
strategy:
fail-fast: false
matrix:
include:
- test-name: 'Room Management API Tests (Rooms, Meetings)'
test-script: 'test:integration-backend-room-management'
- test-name: 'Rooms API Tests'
test-script: 'test:integration-backend-rooms'
- test-name: 'Webhook Tests'
test-script: 'test:integration-backend-webhooks'
- 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'
- 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'
steps:
- name: Install LK CLI
run: curl -sSL https://get.livekit.io/cli | bash
@ -78,11 +85,13 @@ 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: './meet-ce/backend/reports/'
JEST_JUNIT_OUTPUT_DIR: './backend/reports/'
- name: Upload OpenVidu Meet logs
if: failure()
@ -106,7 +115,7 @@ jobs:
start-aws-runner-s3:
name: Prepare AWS runner (S3)
runs-on: ${{ vars.LABEL_WORKER_SELFHOSTED }}
runs-on: ov-actions-runner
if: ${{ inputs.use-aws != 'false' }}
outputs:
label: ${{ steps.start-ec2-runner.outputs.label }}
@ -129,7 +138,7 @@ jobs:
start-aws-runner-abs:
name: Prepare AWS runner (ABS)
runs-on: ${{ vars.LABEL_WORKER_SELFHOSTED }}
runs-on: ov-actions-runner
if: ${{ inputs.use-aws != 'false' }}
outputs:
label: ${{ steps.start-ec2-runner.outputs.label }}
@ -152,7 +161,7 @@ jobs:
start-aws-runner-gcs:
name: Prepare AWS runner (GCS)
runs-on: ${{ vars.LABEL_WORKER_SELFHOSTED }}
runs-on: ov-actions-runner
if: ${{ inputs.use-aws != 'false' }}
outputs:
label: ${{ steps.start-ec2-runner.outputs.label }}
@ -181,7 +190,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) || vars.LABEL_WORKER_SELFHOSTED }}
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' }}
strategy:
fail-fast: false
matrix:
@ -283,7 +292,7 @@ jobs:
- name: Run tests
run: pnpm run test:integration-backend-recordings
env:
JEST_JUNIT_OUTPUT_DIR: './meet-ce/backend/reports/'
JEST_JUNIT_OUTPUT_DIR: './backend/reports/'
MEET_BLOB_STORAGE_MODE: ${{ matrix.storage-provider }}
# ABS variables
MEET_AZURE_ACCOUNT_NAME: ${{ matrix.storage-provider == 'abs' && vars.MEET_AZURE_ACCOUNT_NAME || '' }}
@ -306,7 +315,7 @@ jobs:
uses: actions/upload-artifact@v4
with:
name: recordings-test-${{ matrix.storage-provider }}-openvidu-meet-logs
path: meet_backend.log
path: backend/meet_backend.log
retention-days: 2
- name: Clean up credentials
@ -326,7 +335,7 @@ jobs:
- start-aws-runner-abs
- start-aws-runner-gcs
- test-recordings
runs-on: ${{ vars.LABEL_WORKER_SELFHOSTED }}
runs-on: ov-actions-runner
if: ${{ always() }}
strategy:
fail-fast: false
@ -350,7 +359,7 @@ jobs:
- build-components
- test-api
- test-recordings
runs-on: ${{ vars.LABEL_WORKER_SELFHOSTED }}
runs-on: ov-actions-runner
if: ${{ always() }}
steps:
- name: Remove Artifact

View File

@ -7,7 +7,7 @@ on:
jobs:
unit-test:
name: Backend Unit Tests
runs-on: ${{ vars.LABEL_WORKER_SELFHOSTED }}
runs-on: ov-actions-runner
steps:
- name: Setup Node.js
uses: actions/setup-node@v5

View File

@ -7,7 +7,7 @@ on:
jobs:
e2e-tests:
name: WebComponent E2E Tests
runs-on: ${{ vars.LABEL_WORKER_SELFHOSTED }}
runs-on: ov-actions-runner
steps:
- name: Setup Node.js
uses: actions/setup-node@v5
@ -17,8 +17,6 @@ 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:
@ -58,7 +56,7 @@ jobs:
uses: actions/upload-artifact@v4
with:
name: room-test-results
path: meet-ce/frontend/webcomponent/test-results/
path: frontend/webcomponent/test-results/
retention-days: 2
- name: Upload TestApp logs on failure
@ -66,7 +64,7 @@ jobs:
uses: actions/upload-artifact@v4
with:
name: room-test-testapp-logs
path: testapp.log
path: testapp/testapp.log
retention-days: 2
- name: Upload OpenVidu Meet logs on failure
@ -74,7 +72,7 @@ jobs:
uses: actions/upload-artifact@v4
with:
name: room-test-openvidu-meet-logs
path: meet_backend.log
path: backend/meet_backend.log
retention-days: 2
- name: Clean up

View File

@ -7,7 +7,7 @@ on:
jobs:
unit-test:
name: WebComponent Unit Tests
runs-on: ${{ vars.LABEL_WORKER_SELFHOSTED }}
runs-on: ov-actions-runner
steps:
- name: Setup Node.js
uses: actions/setup-node@v5

2
.gitignore vendored
View File

@ -37,7 +37,7 @@ pnpm-debug.log*
**/**/test-results
**/backend/public/
**/**/public/
**/*/coverage
**/**/test-results

13
.npmrc
View File

@ -1,10 +1,18 @@
# 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 - neccessary for some packages
# Shamefully hoist - necessary for some packages
shamefully-hoist=true
# Node linker - use hoisted for full compatibility
@ -12,6 +20,3 @@ node-linker=hoisted
# Lockfile settings
lockfile=true
# Optional: Store location (uncomment if you want to customize)
# store-dir=.pnpm-store

View File

@ -0,0 +1,65 @@
# 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

65
CONFIG-ACCESO-PUBLICO.md Normal file
View File

@ -0,0 +1,65 @@
# 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
```

36
CONFIG-RED-LOCAL.md Normal file
View File

@ -0,0 +1,36 @@
# 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

107
DOMINIO-PROPIO-CONFIG.md Normal file
View File

@ -0,0 +1,107 @@
# 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
```

72
DUCKDNS-CONFIG.md Normal file
View File

@ -0,0 +1,72 @@
# 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 Normal file
View File

@ -0,0 +1,67 @@
# 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"]

52
EASYPANEL-COMPARACION.md Normal file
View File

@ -0,0 +1,52 @@
# 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.

113
EASYPANEL-README.md Normal file
View File

@ -0,0 +1,113 @@
# 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

164
EASYPANEL-SIMPLE.md Normal file
View File

@ -0,0 +1,164 @@
# 🚀 **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!** 🎉

104
EASYPANEL-TURN-SERVER.md Normal file
View File

@ -0,0 +1,104 @@
# 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

View File

@ -0,0 +1,29 @@
# 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

129
EASYPANEL-VPS-HIBRIDO.md Normal file
View File

@ -0,0 +1,129 @@
# 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

180
LIVEKIT-SELFHOST-SERVER.md Normal file
View File

@ -0,0 +1,180 @@
# 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
```

40
LIVEKIT-TURN-CONFIG.md Normal file
View File

@ -0,0 +1,40 @@
# 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
```

View File

@ -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,9 +46,10 @@ 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
@ -81,7 +82,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:6081](http://localhost:6081).
> **Note:** Livereload is also available at [http://localhost:5080](http://localhost:5080).
## Development
@ -94,7 +95,6 @@ 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,7 +103,6 @@ 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
@ -171,7 +170,6 @@ 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)
@ -227,9 +225,8 @@ 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**: `meet-ce/backend/public/openapi/public.html`
- **REST API**: `backend/public/openapi/public.html`
If you specify an output directory, the documentation will be copied there.
@ -292,41 +289,35 @@ openvidu-meet/
│ ├── src/
│ │ ├── api-key.ts
│ │ ├── auth-config.ts
│ │ ├── room.ts
│ │ ├── participant.ts
│ │ ├── event.model.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
│ │ ├── 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
│ │ ├── middleware/ # Express middleware
│ │ └── 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)
```
@ -421,7 +412,6 @@ 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/).

103
ROUTER-CONFIG-LIVEKIT.md Normal file
View File

@ -0,0 +1,103 @@
# 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`

106
UDP-FIREWALL-CONFIG.md Normal file
View File

@ -0,0 +1,106 @@
# 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

190
configure-livekit-domain.sh Executable file
View File

@ -0,0 +1,190 @@
#!/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

58
configure-udp-ports.sh Executable file
View File

@ -0,0 +1,58 @@
#!/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

136
configure-udp-security.sh Executable file
View File

@ -0,0 +1,136 @@
#!/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"

100
deploy-easypanel.sh Executable file
View File

@ -0,0 +1,100 @@
#!/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]"

221
deploy-livekit-selfhost.sh Executable file
View File

@ -0,0 +1,221 @@
#!/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

125
deploy-local-with-udp.sh Executable file
View File

@ -0,0 +1,125 @@
#!/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

View File

@ -0,0 +1,94 @@
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

70
docker-compose.yml Normal file
View File

@ -0,0 +1,70 @@
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

178
docs/javascript-snippets.md Normal file
View File

@ -0,0 +1,178 @@
# 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*',
}
```

202
docs/typescript-snippets.md Normal file
View File

@ -0,0 +1,202 @@
# 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*',
}
```

68
fix-admin-login.sh Executable file
View File

@ -0,0 +1,68 @@
#!/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"

46
livekit-local.yaml Normal file
View File

@ -0,0 +1,46 @@
# 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 Normal file
View File

@ -0,0 +1,41 @@
# 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

View File

@ -1,9 +1,8 @@
{
"jest.jestCommandLine": "node --experimental-vm-modules ../../node_modules/.bin/jest --config jest.integration.config.mjs",
"jest.jestCommandLine": "node --experimental-vm-modules ../../node_modules/.bin/jest",
"jest.rootPath": "backend",
"jest.nodeEnv": {
"NODE_OPTIONS": "--experimental-vm-modules"
},
"jest.runMode": "on-demand"
}

View File

@ -1,4 +1,39 @@
USE_HTTPS=false
USE_HTTPS=true
MEET_LOG_LEVEL=debug
SERVER_CORS_ORIGIN=*
MEET_INITIAL_API_KEY=meet-api-key
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

View File

@ -1,7 +1,6 @@
USE_HTTPS=false
MEET_LOG_LEVEL=debug
MEET_LOG_LEVEL=verbose
SERVER_CORS_ORIGIN=*
MEET_INITIAL_API_KEY=meet-api-key
MEET_INITIAL_WEBHOOK_ENABLED=true
MEET_INITIAL_WEBHOOK_URL=http://localhost:5080/webhook
MEETING_DEPARTURE_TIMEOUT=1s
MEET_INITIAL_WEBHOOK_URL=http://localhost:5080/webhook

View File

@ -23,26 +23,9 @@ pnpm install
pnpm run build:prod
```
## Storage Architecture
## Storage Structure
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:
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:
### Bucket Structure
@ -126,45 +109,6 @@ 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:
@ -196,10 +140,10 @@ flowchart TD
L -- "Error (recording not found, already stopped,\nor unknown error)" --> O["Reject Request"] --> J
```
3. **Failure handling**:
4. **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 15 minutes.
The garbage collector runs when the OpenVidu deployment starts, and then every 30 minutes.
```mermaid
graph TD;
@ -225,53 +169,46 @@ graph TD;
L --> M
M -->|More rooms| E
M -->|No more rooms| N[Process completed]
```
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).
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).
```mermaid
graph TD;
A[Initiate stale recordings cleanup] --> B[Get all active recordings from database<br/>ACTIVE or ENDING status]
A[Initiate stale recordings cleanup] --> B[Get all in-progress recordings from LiveKit]
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 recordingId, roomId and egressId]
G --> H[Check for corresponding egress in LiveKit]
F --> G[Extract recording ID and updatedAt]
G --> H[Get recording status from storage]
H -->|No egress found| I[Recording is stale - no egress exists]
H -->|Egress exists| J[Extract updatedAt from egress]
H -->|Recording already ABORTED| I[Mark as already processed]
H -->|Recording active| J[Check if updatedAt exists]
I --> K[Update status to ABORTED in database]
K --> L[Log successful abort - no egress found]
J -->|No updatedAt timestamp| K[Keep as fresh - log warning]
J -->|Has updatedAt| L[Calculate if stale]
J -->|No updatedAt timestamp| M[Keep as fresh - log warning]
J -->|Has updatedAt| N[Check if recording age is stale]
L -->|Still fresh| M[Log as fresh]
L -->|Is stale| N[Abort stale recording]
N -->|Age not stale| O[Log as fresh]
N -->|Age is stale| P[Check room existence]
N --> O[Update status to ABORTED in storage]
N --> P[Stop egress in LiveKit]
O --> Q[Log successful abort]
P --> Q
P -->|Room does not exist| Q[Mark as stale]
P -->|Room exists| R[Check if room has participants]
I --> R[Continue to next recording]
K --> R
M --> R
Q --> R
R -->|No participants| Q
R -->|Has participants| O
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]
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]
```

View File

@ -1,15 +1,9 @@
// 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/controllers/index.js';
export * from './src/services/index.js';
export * from './src/models/index.js';
export * from './src/helpers/index.js';
export * from './src/environment.js';
export * from './src/config/index.js';
export * from './src/middlewares/index.js';
export * from './src/utils/index.js';

View File

@ -15,19 +15,16 @@ 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
}]
}
};

View File

@ -1,12 +0,0 @@
import baseConfig from './jest.config.mjs';
const integrationConfig = {
...baseConfig,
runInBand: true,
forceExit: true,
detectOpenHandles: true,
testMatch: ['**/tests/integration/**/*.(spec|test).ts'],
};
export default integrationConfig;

View File

@ -0,0 +1,8 @@
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

View File

@ -0,0 +1,6 @@
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'

View File

@ -0,0 +1,6 @@
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'

View File

@ -0,0 +1,6 @@
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'

View File

@ -0,0 +1,6 @@
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'

View File

@ -0,0 +1,7 @@
name: participantIdentity
in: path
required: true
description: The identity of the participant.
schema:
type: string
example: 'Alice'

View File

@ -1,6 +1,6 @@
name: secret
in: path
required: true
description: The secret value from the room URL used to access the room.
description: The secret value from the room URL used to connect to the room.
schema:
type: string

View File

@ -0,0 +1,12 @@
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']

View File

@ -1,7 +1,12 @@
name: status
in: query
required: false
description: Filter recordings by their status.
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.
schema:
type: string
enum:

View File

@ -2,8 +2,7 @@ name: roomName
in: query
required: false
description: >
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'.
The name of the room.
schema:
type: string
example: 'room'

View File

@ -1,10 +0,0 @@
name: status
in: query
required: false
description: Filter rooms by their status.
schema:
type: string
enum:
- open
- active_meeting
- closed

View File

@ -1,6 +0,0 @@
name: sortField
in: query
required: false
description: The field by which to sort the results.
schema:
type: string

View File

@ -1,9 +0,0 @@
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

View File

@ -1,6 +0,0 @@
description: Create AI assistant activation request
required: true
content:
application/json:
schema:
$ref: '../../schemas/internal/ai-assistant-create-request.yaml'

View File

@ -0,0 +1,6 @@
description: Participant details
required: true
content:
application/json:
schema:
$ref: '../../schemas/internal/meet-participant-options.yaml'

View File

@ -1,6 +0,0 @@
description: Room member token options
required: true
content:
application/json:
schema:
$ref: '../../schemas/internal/room-member-token-options.yaml'

View File

@ -0,0 +1,11 @@
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'

View File

@ -1,35 +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'
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'

View File

@ -11,7 +11,6 @@ content:
chat:
enabled: true
recording:
enabled: true
encoding: H264_720P_30
enabled: false
virtualBackground:
enabled: true

View File

@ -1,17 +0,0 @@
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.

View File

@ -1,8 +0,0 @@
description: Room not found
content:
application/json:
schema:
$ref: '../schemas/error.yaml'
example:
error: 'Room Error'
message: 'Room "room_123" has an active meeting'

View File

@ -1,4 +1,4 @@
description: Forbidden - Insufficient Permissions
description: Forbidden — Insufficient permissions
content:
application/json:
schema:

View File

@ -1,16 +0,0 @@
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'

View File

@ -0,0 +1,8 @@
description: Invalid participant role provided
content:
application/json:
schema:
$ref: '../../schemas/error.yaml'
example:
error: Participant Error
message: 'No valid participant role provided'

View File

@ -0,0 +1,8 @@
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"'

View File

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

View File

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

View File

@ -0,0 +1,8 @@
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'

View File

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

View File

@ -1,5 +0,0 @@
description: Successfully created or reused AI assistant activation
content:
application/json:
schema:
$ref: '../../schemas/internal/ai-assistant-create-response.yaml'

View File

@ -0,0 +1,13 @@
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.

View File

@ -0,0 +1,13 @@
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.

View File

@ -1,10 +0,0 @@
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.

View File

@ -1,5 +0,0 @@
description: Analytics data retrieved successfully
content:
application/json:
schema:
$ref: '../../schemas/internal/meet-analytics.yaml'

View File

@ -1,5 +0,0 @@
description: Successfully retrieved captions config
content:
application/json:
schema:
$ref: '../../schemas/internal/global-captions-config.yaml'

View File

@ -2,4 +2,4 @@ description: Successfully retrieved user profile
content:
application/json:
schema:
$ref: '../../schemas/internal/meet-user.yaml'
$ref: '../../schemas/internal/user.yaml'

View File

@ -2,4 +2,4 @@ description: Successfully retrieved the room role and associated permissions
content:
application/json:
schema:
$ref: '../../schemas/internal/room-member-role-permissions.yaml'
$ref: '../../schemas/internal/meet-room-role-permissions.yaml'

View File

@ -4,7 +4,7 @@ content:
schema:
type: array
items:
$ref: '../../schemas/internal/room-member-role-permissions.yaml'
$ref: '../../schemas/internal/meet-room-role-permissions.yaml'
example:
- role: 'moderator'
permissions:
@ -17,8 +17,6 @@ content:
canUpdateOwnMetadata: true
openvidu:
canRecord: true
canRetrieveRecordings: true
canDeleteRecordings: true
canChat: true
canChangeVirtualBackground: true
- role: 'speaker'
@ -32,7 +30,5 @@ content:
canUpdateOwnMetadata: true
openvidu:
canRecord: false
canRetrieveRecordings: true
canDeleteRecordings: false
canChat: true
canChangeVirtualBackground: true

View File

@ -6,4 +6,4 @@ content:
properties:
message:
type: string
example: Participant 'Alice' kicked successfully from meeting in room 'room-123'
example: Participant 'Alice' kicked successfully from room 'room-123'

View File

@ -1,4 +1,7 @@
description: Successfully refreshed the access token
# headers:
# Set-Cookie:
# $ref: '../../headers/set-cookie-access-token.yaml'
content:
application/json:
schema:

View File

@ -2,13 +2,12 @@ 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:

View File

@ -8,13 +8,12 @@ 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'

View File

@ -1,4 +1,9 @@
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:

View File

@ -1,4 +1,11 @@
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:

View File

@ -11,7 +11,6 @@ content:
roomId: 'room-123'
roomName: 'room'
status: 'complete'
layout: 'grid'
filename: 'room-123--XX445.mp4'
startDate: 1600000000000
endDate: 1600000003600
@ -26,6 +25,5 @@ content:
roomId: 'room-456'
roomName: 'room'
status: 'active'
layout: 'grid'
filename: 'room-456--QR789.mp4'
startDate: 1682500000000

View File

@ -19,7 +19,6 @@ content:
roomId: 'room-123'
roomName: 'room'
status: 'active'
layout: 'grid'
filename: 'room-123--XX445.mp4'
startDate: 1620000000000
endDate: 1620000003600
@ -30,7 +29,6 @@ content:
roomId: 'room-456'
roomName: 'room'
status: 'complete'
layout: 'grid'
filename: 'room-456--XX678.mp4'
startDate: 1625000000000
endDate: 1625000007200

View File

@ -19,15 +19,8 @@ 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
@ -50,21 +43,8 @@ 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

View File

@ -28,15 +28,8 @@ 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
@ -53,21 +46,8 @@ 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
@ -100,8 +80,6 @@ content:
enabled: false
virtualBackground:
enabled: true
e2ee:
enabled: false
- roomId: 'room-456'
roomName: 'room'
creationDate: 1620001000000
@ -113,8 +91,6 @@ content:
enabled: true
virtualBackground:
enabled: false
e2ee:
enabled: false
pagination:
isTruncated: true
nextPageToken: 'abc123'

View File

@ -1,10 +0,0 @@
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

View File

@ -0,0 +1,12 @@
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

View File

@ -1,10 +0,0 @@
description: Success response for updating room status
content:
application/json:
schema:
type: object
properties:
message:
type: string
example:
message: Room 'room-123' closed successfully

View File

@ -1,37 +0,0 @@
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

View File

@ -1,14 +0,0 @@
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

View File

@ -1,8 +0,0 @@
type: object
properties:
enabled:
type: boolean
description: Indicates whether captions are enabled in the system
example: true
required:
- enabled

View File

@ -1,24 +0,0 @@
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