From 5e7de6c94325af034a2406c396992993c6135dd5 Mon Sep 17 00:00:00 2001 From: killer-cf Date: Fri, 28 Nov 2025 09:20:23 -0300 Subject: [PATCH] chore: Remove Jitsi and theme documentation files and refine backend gitignore rules. --- CORRECOES_JITSI.md | 296 --------------- PLANO_IMPLEMENTACAO_JITSI.md | 701 ----------------------------------- RELATORIO_TESTES_TEMAS.md | 117 ------ VALIDACAO_TEMAS.md | 89 ----- packages/backend/.gitignore | 3 +- 5 files changed, 1 insertion(+), 1205 deletions(-) delete mode 100644 CORRECOES_JITSI.md delete mode 100644 PLANO_IMPLEMENTACAO_JITSI.md delete mode 100644 RELATORIO_TESTES_TEMAS.md delete mode 100644 VALIDACAO_TEMAS.md diff --git a/CORRECOES_JITSI.md b/CORRECOES_JITSI.md deleted file mode 100644 index 28a39a1..0000000 --- a/CORRECOES_JITSI.md +++ /dev/null @@ -1,296 +0,0 @@ -# Correções Implementadas para Integração Jitsi - -## Resumo das Alterações - -Este documento descreve todas as correções implementadas para integrar o servidor Jitsi ao projeto SGSE e fazer as chamadas de áudio e vídeo funcionarem corretamente. - ---- - -## 1. Configuração do JitsiConnection - -### Problema Identificado -- A configuração do `serviceUrl` e `muc` estava incorreta para Docker Jitsi local -- O domínio incluía a porta, causando problemas na conexão - -### Correção Implementada -```typescript -// Separar host e porta corretamente -const { host, porta } = obterHostEPorta(config.domain); -const protocol = config.useHttps ? 'https' : 'http'; - -const options = { - hosts: { - domain: host, // Apenas o host (sem porta) - muc: `conference.${host}` // MUC no mesmo domínio - }, - serviceUrl: `${protocol}://${host}:${porta}/http-bind`, // BOSH com porta - bosh: `${protocol}://${host}:${porta}/http-bind`, // BOSH alternativo - clientNode: config.appId -}; -``` - -**Arquivo modificado:** -- `apps/web/src/lib/components/call/CallWindow.svelte` - -**Arquivo criado/atualizado:** -- `apps/web/src/lib/utils/jitsi.ts` - Adicionada função `obterHostEPorta()` - ---- - -## 2. Criação de Tracks Locais - -### Problema Identificado -- Os tracks locais não estavam sendo criados após entrar na conferência -- Faltava o evento `CONFERENCE_JOINED` para criar tracks locais - -### Correção Implementada -```typescript -conference.on(JitsiMeetJS.constants.events.conference.CONFERENCE_JOINED, async () => { - // Criar tracks locais com constraints apropriadas - const constraints = { - audio: estadoAtual.audioHabilitado ? { - echoCancellation: true, - noiseSuppression: true, - autoGainControl: true - } : false, - video: estadoAtual.videoHabilitado ? { - facingMode: 'user', - width: { ideal: 1280 }, - height: { ideal: 720 } - } : false - }; - - const tracks = await JitsiMeetJS.createLocalTracks(constraints, { - devices: [], - cameraDeviceId: estadoChamada.dispositivos.cameraId || undefined, - micDeviceId: estadoChamada.dispositivos.microphoneId || undefined - }); - - // Adicionar tracks à conferência e anexar ao vídeo local - for (const track of tracks) { - await conference.addTrack(track); - if (track.getType() === 'video' && localVideo) { - track.attach(localVideo); - } - } -}); -``` - -**Arquivo modificado:** -- `apps/web/src/lib/components/call/CallWindow.svelte` - ---- - -## 3. Gerenciamento de Tracks - -### Problema Identificado -- Tracks locais não eram armazenados corretamente -- Falta de limpeza adequada ao finalizar chamada - -### Correção Implementada -- Adicionada variável de estado `localTracks: JitsiTrack[]` para rastrear todos os tracks locais -- Implementada limpeza adequada no método `finalizar()`: - - Desconectar tracks antes de liberar - - Dispor de todos os tracks locais - - Limpar referências - -**Arquivo modificado:** -- `apps/web/src/lib/components/call/CallWindow.svelte` - ---- - -## 4. Attach/Detach de Tracks Remotos - -### Problema Identificado -- Tracks remotos não eram anexados corretamente aos elementos de vídeo/áudio -- Não havia tratamento específico para áudio vs vídeo - -### Correção Implementada -```typescript -function adicionarTrackRemoto(track: JitsiTrack): void { - const participantId = track.getParticipantId(); - const trackType = track.getType(); - - if (trackType === 'audio') { - // Criar elemento de áudio invisível - const audioElement = document.createElement('audio'); - audioElement.id = `remote-audio-${participantId}`; - audioElement.autoplay = true; - track.attach(audioElement); - videoContainer.appendChild(audioElement); - } else if (trackType === 'video') { - // Criar elemento de vídeo - const videoElement = document.createElement('video'); - videoElement.id = `remote-video-${participantId}`; - videoElement.autoplay = true; - track.attach(videoElement); - videoContainer.appendChild(videoElement); - } -} -``` - -**Arquivo modificado:** -- `apps/web/src/lib/components/call/CallWindow.svelte` - ---- - -## 5. Controles de Áudio e Vídeo - -### Problema Identificado -- Os métodos `handleToggleAudio` e `handleToggleVideo` não criavam novos tracks quando necessário -- Não atualizavam corretamente o estado dos tracks locais - -### Correção Implementada -- Implementada lógica para criar tracks se não existirem -- Atualização correta do estado dos tracks (mute/unmute) -- Sincronização com o backend quando anfitrião -- Anexar/desanexar tracks ao vídeo local corretamente - -**Arquivo modificado:** -- `apps/web/src/lib/components/call/CallWindow.svelte` - ---- - -## 6. Tratamento de Erros - -### Problema Identificado -- Uso de `alert()` para erros (não amigável) -- Falta de mensagens de erro claras - -### Correção Implementada -- Implementado sistema de tratamento de erros com `ErrorModal` -- Integrado com `traduzirErro()` para mensagens amigáveis -- Adicionado estado de erro no componente: - ```typescript - let showErrorModal = $state(false); - let errorTitle = $state('Erro na Chamada'); - let errorMessage = $state(''); - let errorDetails = $state(undefined); - ``` - -**Arquivos modificados:** -- `apps/web/src/lib/components/call/CallWindow.svelte` -- Integração com `apps/web/src/lib/utils/erroHelpers.ts` - ---- - -## 7. Inicialização do Jitsi Meet JS - -### Problema Identificado -- Configuração básica do Jitsi pode estar incompleta -- Nível de log muito restritivo - -### Correção Implementada -```typescript -JitsiMeetJS.init({ - disableAudioLevels: false, // Habilitado para melhor qualidade - disableSimulcast: false, - enableWindowOnErrorHandler: true, - enableRemb: true, // REMB para controle de bitrate - enableTcc: true, // TCC para controle de congestionamento - disableThirdPartyRequests: false -}); - -JitsiMeetJS.setLogLevel(JitsiMeetJS.constants.logLevels.INFO); // Mais verboso para debug -``` - -**Arquivo modificado:** -- `apps/web/src/lib/components/call/CallWindow.svelte` - ---- - -## 8. UI/UX Melhorias - -### Implementado -- Indicador de conexão durante estabelecimento da chamada -- Mensagem de "Conectando..." enquanto não há conexão estabelecida -- Tratamento visual adequado de estados de conexão - -**Arquivo modificado:** -- `apps/web/src/lib/components/call/CallWindow.svelte` - ---- - -## 9. Eventos da Conferência - -### Adicionado -- `CONFERENCE_JOINED`: Criar tracks locais após entrar -- `CONFERENCE_LEFT`: Limpar tracks ao sair -- Melhor tratamento de `TRACK_ADDED` e `TRACK_REMOVED` - -**Arquivo modificado:** -- `apps/web/src/lib/components/call/CallWindow.svelte` - ---- - -## 10. Correção de Interfaces TypeScript - -### Adicionado -- Método `addTrack()` na interface `JitsiConference` -- Melhor tipagem de `JitsiTrack` com propriedade `track: MediaStreamTrack` - -**Arquivo modificado:** -- `apps/web/src/lib/components/call/CallWindow.svelte` - ---- - -## Configuração Necessária - -### Variáveis de Ambiente (.env) -```env -# Jitsi Meet Configuration (Docker Local) -VITE_JITSI_DOMAIN=localhost:8443 -VITE_JITSI_APP_ID=sgse-app -VITE_JITSI_ROOM_PREFIX=sgse -VITE_JITSI_USE_HTTPS=true -``` - -**Nota:** Para Docker Jitsi local, geralmente usa-se HTTPS na porta 8443. - ---- - -## Verificações Necessárias - -### 1. Docker Jitsi Rodando -```bash -docker ps | grep jitsi -``` - -### 2. Porta 8443 Acessível -```bash -curl -k https://localhost:8443 -``` - -### 3. Permissões do Navegador -- Microfone deve estar permitido -- Câmera deve estar permitida (para chamadas de vídeo) - -### 4. Logs do Navegador -- Abrir DevTools (F12) -- Verificar Console para erros de conexão -- Verificar Network para erros de rede - ---- - -## Próximos Passos (Se Necessário) - -1. **Testar conectividade** - Verificar se o servidor Jitsi responde corretamente -2. **Ajustar configuração de rede** - Se houver problemas de firewall ou CORS -3. **Configurar STUN/TURN** - Para conexões através de NAT (se necessário) -4. **Otimizar qualidade** - Ajustar bitrates e resoluções conforme necessário - ---- - -## Status - -✅ **Todas as correções foram implementadas** -✅ **Código sem erros de lint** -✅ **Tratamento de erros adequado** -✅ **Interfaces TypeScript corretas** -✅ **Gerenciamento de recursos adequado** - ---- - -**Data:** $(date) -**Versão:** 1.0.0 - diff --git a/PLANO_IMPLEMENTACAO_JITSI.md b/PLANO_IMPLEMENTACAO_JITSI.md deleted file mode 100644 index 4b05f03..0000000 --- a/PLANO_IMPLEMENTACAO_JITSI.md +++ /dev/null @@ -1,701 +0,0 @@ -# Plano de Implementação - Chamadas de Áudio e Vídeo com Jitsi Meet - -## Opção Escolhida: Docker Local (Desenvolvimento) - ---- - -## 📋 Etapas Fora do Código - Configuração Docker - -### Etapa 1: Preparar Ambiente Docker - -**Requisitos:** - -- Docker Desktop instalado e rodando -- Mínimo 4GB RAM disponível -- Portas livres: 8000, 8443, 10000-20000/udp - -**Passos:** - -1. **Criar diretório para configuração Docker Jitsi:** - -```bash -mkdir -p ~/jitsi-docker -cd ~/jitsi-docker -``` - -2. **Clonar repositório oficial:** - -```bash -git clone https://github.com/jitsi/docker-jitsi-meet.git -cd docker-jitsi-meet -``` - -3. **Configurar variáveis de ambiente:** - -```bash -cp env.example .env -``` - -4. **Editar arquivo `.env` com as seguintes configurações:** - -```env -# Configuração básica para desenvolvimento local -CONFIG=~/.jitsi-meet-cfg -TZ=America/Recife - -# Desabilitar Let's Encrypt (não necessário para localhost) -ENABLE_LETSENCRYPT=0 - -# Portas HTTP/HTTPS -HTTP_PORT=8000 -HTTPS_PORT=8443 - -# Domínio local -PUBLIC_URL=http://localhost:8000 -DOMAIN=localhost - -# Desabilitar autenticação para facilitar testes -ENABLE_AUTH=0 -ENABLE_GUESTS=1 - -# Desabilitar transcrissão (não necessário para desenvolvimento) -ENABLE_TRANSCRIPTION=0 - -# Desabilitar gravação no servidor (usaremos gravação local) -ENABLE_RECORDING=0 - -# Configurações de vídeo (ajustar conforme necessidade) -ENABLE_PREJOIN_PAGE=0 -START_AUDIO_MUTED=0 -START_VIDEO_MUTED=0 - -# Configurações de segurança -ENABLE_XMPP_WEBSOCKET=0 -ENABLE_P2P=1 - -# Limites -MAX_NUMBER_OF_PARTICIPANTS=10 -RESOLUTION_WIDTH=1280 -RESOLUTION_HEIGHT=720 -``` - -5. **Criar diretórios necessários:** - -```bash -mkdir -p ~/.jitsi-meet-cfg/{web/letsencrypt,transcripts,prosody/config,prosody/prosody-plugins-custom,jicofo,jvb} -``` - -6. **Iniciar containers:** - -```bash -docker-compose up -d -``` - -7. **Verificar status:** - -```bash -docker-compose ps -``` - -8. **Ver logs se necessário:** - -```bash -docker-compose logs -f -``` - -9. **Testar acesso:** - -- Acessar: http://localhost:8000 -- Criar uma sala de teste e verificar se funciona - -**Troubleshooting:** - -- Se houver erro de permissão nos diretórios: `sudo chown -R $USER:$USER ~/.jitsi-meet-cfg` -- Se portas estiverem em uso, alterar HTTP_PORT e HTTPS_PORT no .env -- Para parar: `docker-compose down` -- Para reiniciar: `docker-compose restart` - ---- - -## 📦 Etapas no Código - Backend Convex - -### Etapa 2: Atualizar Schema - -**Arquivo:** `packages/backend/convex/schema.ts` - -**Adicionar nova tabela `chamadas`:** - -```typescript -chamadas: defineTable({ - conversaId: v.id('conversas'), - tipo: v.union(v.literal('audio'), v.literal('video')), - roomName: v.string(), // Nome único da sala Jitsi - criadoPor: v.id('usuarios'), // Anfitrião/criador - participantes: v.array(v.id('usuarios')), - status: v.union( - v.literal('aguardando'), - v.literal('em_andamento'), - v.literal('finalizada'), - v.literal('cancelada') - ), - iniciadaEm: v.optional(v.number()), - finalizadaEm: v.optional(v.number()), - duracaoSegundos: v.optional(v.number()), - gravando: v.boolean(), - gravacaoIniciadaPor: v.optional(v.id('usuarios')), - gravacaoIniciadaEm: v.optional(v.number()), - gravacaoFinalizadaEm: v.optional(v.number()), - configuracoes: v.optional( - v.object({ - audioHabilitado: v.boolean(), - videoHabilitado: v.boolean(), - participantesConfig: v.optional( - v.array( - v.object({ - usuarioId: v.id('usuarios'), - audioHabilitado: v.boolean(), - videoHabilitado: v.boolean(), - forcadoPeloAnfitriao: v.optional(v.boolean()) // Se foi forçado pelo anfitrião - }) - ) - ) - }) - ), - criadoEm: v.number() -}) - .index('by_conversa', ['conversaId', 'status']) - .index('by_conversa_ativa', ['conversaId', 'status']) - .index('by_criado_por', ['criadoPor']) - .index('by_status', ['status']) - .index('by_room_name', ['roomName']); -``` - -### Etapa 3: Criar Backend de Chamadas - -**Arquivo:** `packages/backend/convex/chamadas.ts` - -**Funções a implementar:** - -#### Mutations: - -1. `criarChamada` - Criar nova chamada -2. `iniciarChamada` - Marcar como em andamento -3. `finalizarChamada` - Finalizar e calcular duração -4. `adicionarParticipante` - Adicionar participante -5. `removerParticipante` - Remover participante -6. `toggleAudioVideo` - Anfitrião controla áudio/vídeo de participante -7. `atualizarConfiguracaoParticipante` - Atualizar configuração individual -8. `iniciarGravacao` - Marcar início de gravação -9. `finalizarGravacao` - Marcar fim de gravação - -#### Queries: - -1. `obterChamadaAtiva` - Buscar chamada ativa de uma conversa -2. `listarChamadas` - Listar histórico -3. `verificarAnfitriao` - Verificar se usuário é anfitrião -4. `obterParticipantesChamada` - Listar participantes - -**Tipos TypeScript (sem usar `any`):** - -```typescript -import type { Id } from './_generated/dataModel'; -import type { QueryCtx, MutationCtx } from './_generated/server'; - -type ChamadaTipo = 'audio' | 'video'; -type ChamadaStatus = 'aguardando' | 'em_andamento' | 'finalizada' | 'cancelada'; - -interface ParticipanteConfig { - usuarioId: Id<'usuarios'>; - audioHabilitado: boolean; - videoHabilitado: boolean; - forcadoPeloAnfitriao?: boolean; -} - -interface ConfiguracoesChamada { - audioHabilitado: boolean; - videoHabilitado: boolean; - participantesConfig?: ParticipanteConfig[]; -} -``` - ---- - -## 🎨 Etapas no Código - Frontend Svelte - -### Etapa 4: Instalar Dependências - -**Arquivo:** `apps/web/package.json` - -```bash -cd apps/web -bun add lib-jitsi-meet -``` - -**Dependências adicionais necessárias:** - -- `lib-jitsi-meet` - Biblioteca oficial Jitsi -- (Possivelmente tipos) `@types/lib-jitsi-meet` se disponível - -### Etapa 5: Configurar Variáveis de Ambiente - -**Arquivo:** `apps/web/.env` - -```env -# Jitsi Meet Configuration (Docker Local) -VITE_JITSI_DOMAIN=localhost:8443 -VITE_JITSI_APP_ID=sgse-app -VITE_JITSI_ROOM_PREFIX=sgse -VITE_JITSI_USE_HTTPS=false -``` - -### Etapa 6: Criar Utilitários Jitsi - -**Arquivo:** `apps/web/src/lib/utils/jitsi.ts` - -**Funções:** - -- `gerarRoomName(conversaId: string, tipo: "audio" | "video"): string` - Gerar nome único da sala -- `obterConfiguracaoJitsi()` - Retornar configuração do Jitsi baseada em .env -- `validarDispositivos()` - Validar disponibilidade de microfone/webcam -- `obterDispositivosDisponiveis()` - Listar dispositivos de mídia - -**Tipos (sem `any`):** - -```typescript -interface ConfiguracaoJitsi { - domain: string; - appId: string; - roomPrefix: string; - useHttps: boolean; -} - -interface DispositivoMedia { - deviceId: string; - label: string; - kind: 'audioinput' | 'audiooutput' | 'videoinput'; -} - -interface DispositivosDisponiveis { - microphones: DispositivoMedia[]; - speakers: DispositivoMedia[]; - cameras: DispositivoMedia[]; -} -``` - -### Etapa 7: Criar Store de Chamadas - -**Arquivo:** `apps/web/src/lib/stores/callStore.ts` - -**Estado gerenciado:** - -- Chamada ativa (se houver) -- Estado de mídia (áudio/vídeo ligado/desligado) -- Dispositivos selecionados -- Status de gravação -- Lista de participantes -- Duração da chamada -- É anfitrião ou não - -**Tipos:** - -```typescript -interface EstadoChamada { - chamadaId: Id<'chamadas'> | null; - conversaId: Id<'conversas'> | null; - tipo: 'audio' | 'video' | null; - roomName: string | null; - estaConectado: boolean; - audioHabilitado: boolean; - videoHabilitado: boolean; - gravando: boolean; - ehAnfitriao: boolean; - participantes: Array<{ - usuarioId: Id<'usuarios'>; - nome: string; - avatar?: string; - audioHabilitado: boolean; - videoHabilitado: boolean; - }>; - duracaoSegundos: number; - dispositivos: { - microphoneId: string | null; - cameraId: string | null; - speakerId: string | null; - }; -} - -interface EventosChamada { - 'participant-joined': (participant: ParticipanteJitsi) => void; - 'participant-left': (participantId: string) => void; - 'audio-mute-status-changed': (isMuted: boolean) => void; - 'video-mute-status-changed': (isMuted: boolean) => void; - 'connection-failed': (error: Error) => void; - 'connection-disconnected': () => void; -} -``` - -**Métodos principais:** - -- `iniciarChamada(conversaId, tipo)` -- `finalizarChamada()` -- `toggleAudio()` -- `toggleVideo()` -- `iniciarGravacao()` -- `finalizarGravacao()` -- `atualizarDispositivos()` - -### Etapa 8: Criar Utilitários de Gravação - -**Arquivo:** `apps/web/src/lib/utils/mediaRecorder.ts` - -**Funções:** - -- `iniciarGravacaoAudio(stream: MediaStream): MediaRecorder` - Gravar apenas áudio -- `iniciarGravacaoVideo(stream: MediaStream): MediaRecorder` - Gravar áudio + vídeo -- `pararGravacao(recorder: MediaRecorder): Promise` - Parar e retornar blob -- `salvarGravacao(blob: Blob, nomeArquivo: string): void` - Salvar localmente -- `obterDuracaoGravacao(recorder: MediaRecorder): number` - Obter duração - -**Tipos:** - -```typescript -interface OpcoesGravacao { - audioBitsPerSecond?: number; - videoBitsPerSecond?: number; - mimeType?: string; -} - -interface ResultadoGravacao { - blob: Blob; - duracaoSegundos: number; - nomeArquivo: string; -} -``` - -### Etapa 9: Criar Componente CallWindow - -**Arquivo:** `apps/web/src/lib/components/call/CallWindow.svelte` - -**Características:** - -- Janela flutuante redimensionável e arrastável -- Integração com lib-jitsi-meet -- Container para vídeo dos participantes -- Barra de controles -- Indicador de gravação -- Contador de duração - -**Props (TypeScript estrito):** - -```typescript -interface Props { - chamadaId: Id<'chamadas'>; - conversaId: Id<'conversas'>; - tipo: 'audio' | 'video'; - roomName: string; - ehAnfitriao: boolean; - onClose: () => void; -} -``` - -**Estrutura:** - -- `