feat: enhance time synchronization and Jitsi configuration handling
- Implemented a comprehensive time synchronization mechanism that applies GMT offsets based on user configuration, ensuring accurate timestamps across the application. - Updated the Jitsi configuration to include SSH settings, allowing for better integration with Docker setups. - Refactored the backend queries and mutations to handle the new SSH configuration fields, ensuring secure and flexible server management. - Enhanced error handling and logging for time synchronization processes, providing clearer feedback for users and developers.
This commit is contained in:
@@ -172,82 +172,17 @@
|
||||
}
|
||||
|
||||
// Carregar Jitsi dinamicamente
|
||||
// Polyfill para BlobBuilder (API antiga que lib-jitsi-meet pode usar)
|
||||
// Deve ser executado antes de qualquer import da biblioteca
|
||||
function adicionarBlobBuilderPolyfill(): void {
|
||||
if (!browser || typeof window === 'undefined') return;
|
||||
|
||||
// Verificar se já foi adicionado (evitar múltiplas execuções)
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
if ((window as any).__blobBuilderPolyfillAdded) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Implementar BlobBuilder usando Blob moderno
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
const BlobBuilderClass = class BlobBuilder {
|
||||
private parts: BlobPart[] = [];
|
||||
|
||||
append(data: BlobPart): void {
|
||||
this.parts.push(data);
|
||||
}
|
||||
|
||||
getBlob(contentType?: string): Blob {
|
||||
return new Blob(this.parts, contentType ? { type: contentType } : undefined);
|
||||
}
|
||||
};
|
||||
|
||||
// Adicionar em todos os possíveis locais onde a biblioteca pode procurar
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
const win = window as any;
|
||||
|
||||
if (typeof win.BlobBuilder === 'undefined') {
|
||||
win.BlobBuilder = BlobBuilderClass;
|
||||
}
|
||||
|
||||
if (typeof win.WebKitBlobBuilder === 'undefined') {
|
||||
win.WebKitBlobBuilder = BlobBuilderClass;
|
||||
}
|
||||
|
||||
if (typeof win.MozBlobBuilder === 'undefined') {
|
||||
win.MozBlobBuilder = BlobBuilderClass;
|
||||
}
|
||||
|
||||
if (typeof win.MSBlobBuilder === 'undefined') {
|
||||
win.MSBlobBuilder = BlobBuilderClass;
|
||||
}
|
||||
|
||||
// Também adicionar no global scope caso a biblioteca procure lá
|
||||
if (typeof globalThis !== 'undefined') {
|
||||
if (typeof (globalThis as any).BlobBuilder === 'undefined') {
|
||||
(globalThis as any).BlobBuilder = BlobBuilderClass;
|
||||
}
|
||||
if (typeof (globalThis as any).WebKitBlobBuilder === 'undefined') {
|
||||
(globalThis as any).WebKitBlobBuilder = BlobBuilderClass;
|
||||
}
|
||||
}
|
||||
|
||||
// Marcar que o polyfill foi adicionado
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
(window as any).__blobBuilderPolyfillAdded = true;
|
||||
|
||||
console.log('✅ Polyfill BlobBuilder adicionado para todos os navegadores');
|
||||
}
|
||||
|
||||
// Executar polyfill imediatamente se estiver no browser
|
||||
// Isso garante que esteja disponível antes de qualquer import
|
||||
if (browser && typeof window !== 'undefined') {
|
||||
adicionarBlobBuilderPolyfill();
|
||||
}
|
||||
|
||||
async function carregarJitsi(): Promise<void> {
|
||||
if (!browser || JitsiMeetJS) return;
|
||||
|
||||
try {
|
||||
console.log('🔄 Tentando carregar lib-jitsi-meet...');
|
||||
|
||||
// Adicionar polyfill antes de carregar a biblioteca
|
||||
adicionarBlobBuilderPolyfill();
|
||||
// Polyfill BlobBuilder já deve estar disponível via app.html
|
||||
// Verificar se está disponível antes de carregar a biblioteca
|
||||
if (typeof (window as any).BlobBuilder === 'undefined') {
|
||||
console.warn('⚠️ Polyfill BlobBuilder não encontrado, pode causar erros');
|
||||
}
|
||||
|
||||
// Tentar carregar o módulo lib-jitsi-meet dinamicamente
|
||||
// Usar import dinâmico para evitar problemas de SSR e permitir carregamento apenas no browser
|
||||
@@ -1000,9 +935,11 @@
|
||||
onMount(async () => {
|
||||
if (!browser) return;
|
||||
|
||||
// Adicionar polyfill BlobBuilder o mais cedo possível
|
||||
// Isso deve ser feito antes de qualquer tentativa de carregar lib-jitsi-meet
|
||||
adicionarBlobBuilderPolyfill();
|
||||
// Polyfill BlobBuilder já deve estar disponível via app.html
|
||||
// Verificar se está disponível
|
||||
if (typeof (window as any).BlobBuilder === 'undefined') {
|
||||
console.warn('⚠️ Polyfill BlobBuilder não encontrado no onMount');
|
||||
}
|
||||
|
||||
// Inicializar store primeiro
|
||||
inicializarStore();
|
||||
|
||||
@@ -190,9 +190,51 @@
|
||||
const informacoesDispositivo = await obterInformacoesDispositivo();
|
||||
coletandoInfo = false;
|
||||
|
||||
// Obter tempo sincronizado
|
||||
const timestamp = await obterTempoServidor(client);
|
||||
const sincronizadoComServidor = true; // Sempre true quando usamos obterTempoServidor
|
||||
// Obter tempo sincronizado e aplicar GMT offset (igual ao relógio)
|
||||
const configRelogio = await client.query(api.configuracaoRelogio.obterConfiguracao, {});
|
||||
// Usar gmtOffset da configuração, sem valor padrão, pois 0 é um valor válido
|
||||
const gmtOffset = configRelogio.gmtOffset ?? 0;
|
||||
|
||||
let timestampBase: number;
|
||||
|
||||
if (configRelogio.usarServidorExterno) {
|
||||
try {
|
||||
const resultado = await client.action(api.configuracaoRelogio.sincronizarTempo, {});
|
||||
if (resultado.sucesso && resultado.timestamp) {
|
||||
timestampBase = resultado.timestamp;
|
||||
} else {
|
||||
timestampBase = await obterTempoServidor(client);
|
||||
}
|
||||
} catch (error) {
|
||||
console.warn('Erro ao sincronizar com servidor externo:', error);
|
||||
if (configRelogio.fallbackParaPC) {
|
||||
timestampBase = Date.now();
|
||||
} else {
|
||||
timestampBase = await obterTempoServidor(client);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Usar relógio do PC (sem sincronização com servidor)
|
||||
timestampBase = Date.now();
|
||||
}
|
||||
|
||||
// Aplicar GMT offset ao timestamp
|
||||
// Quando GMT é 0, compensar o timezone local do navegador para que o timestamp
|
||||
// represente o horário local (não UTC), evitando que apareça 3 horas a mais
|
||||
let timestamp: number;
|
||||
if (gmtOffset !== 0) {
|
||||
// Aplicar offset configurado
|
||||
timestamp = timestampBase + (gmtOffset * 60 * 60 * 1000);
|
||||
} else {
|
||||
// Quando GMT = 0, ajustar para horário local do navegador
|
||||
// getTimezoneOffset() retorna minutos POSITIVOS para fusos ATRÁS de UTC
|
||||
// Exemplo: Brasil (UTC-3) retorna 180 minutos
|
||||
// Subtrair esses minutos para que o timestamp represente o horário local
|
||||
const timezoneOffset = new Date().getTimezoneOffset(); // Offset em minutos
|
||||
timestamp = timestampBase - (timezoneOffset * 60 * 1000); // Subtrair minutos em milissegundos
|
||||
}
|
||||
// Sincronizado apenas se usar servidor externo e sincronização foi bem-sucedida
|
||||
const sincronizadoComServidor = configRelogio.usarServidorExterno && timestampBase !== Date.now();
|
||||
|
||||
// Upload da imagem (obrigatória agora)
|
||||
let imagemId: Id<'_storage'> | undefined = undefined;
|
||||
@@ -275,9 +317,47 @@
|
||||
// Se capturou a foto, mostrar modal de confirmação
|
||||
if (blob && capturandoAutomaticamente) {
|
||||
capturandoAutomaticamente = false;
|
||||
// Obter data e hora sincronizada do servidor
|
||||
// Obter data e hora sincronizada do servidor com GMT offset (igual ao relógio)
|
||||
try {
|
||||
const timestamp = await obterTempoServidor(client);
|
||||
const configRelogio = await client.query(api.configuracaoRelogio.obterConfiguracao, {});
|
||||
// Usar gmtOffset da configuração, sem valor padrão, pois 0 é um valor válido
|
||||
const gmtOffset = configRelogio.gmtOffset ?? 0;
|
||||
|
||||
let timestampBase: number;
|
||||
|
||||
if (configRelogio.usarServidorExterno) {
|
||||
try {
|
||||
const resultado = await client.action(api.configuracaoRelogio.sincronizarTempo, {});
|
||||
if (resultado.sucesso && resultado.timestamp) {
|
||||
timestampBase = resultado.timestamp;
|
||||
} else {
|
||||
timestampBase = await obterTempoServidor(client);
|
||||
}
|
||||
} catch (error) {
|
||||
console.warn('Erro ao sincronizar com servidor externo:', error);
|
||||
if (configRelogio.fallbackParaPC) {
|
||||
timestampBase = Date.now();
|
||||
} else {
|
||||
timestampBase = await obterTempoServidor(client);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Usar relógio do PC (sem sincronização com servidor)
|
||||
timestampBase = Date.now();
|
||||
}
|
||||
|
||||
// Aplicar GMT offset ao timestamp
|
||||
// Quando GMT é 0, usar timestamp UTC puro e deixar toLocaleTimeString() fazer a conversão automática
|
||||
// Quando GMT ≠ 0, aplicar offset configurado ao timestamp
|
||||
let timestamp: number;
|
||||
if (gmtOffset !== 0) {
|
||||
// Aplicar offset configurado
|
||||
timestamp = timestampBase + (gmtOffset * 60 * 60 * 1000);
|
||||
} else {
|
||||
// Quando GMT = 0, manter timestamp UTC puro
|
||||
// O toLocaleTimeString() converterá automaticamente para o timezone local do navegador
|
||||
timestamp = timestampBase;
|
||||
}
|
||||
const dataObj = new Date(timestamp);
|
||||
const data = dataObj.toLocaleDateString('pt-BR');
|
||||
const hora = dataObj.toLocaleTimeString('pt-BR', { hour: '2-digit', minute: '2-digit', second: '2-digit' });
|
||||
|
||||
@@ -17,6 +17,8 @@
|
||||
async function atualizarTempo() {
|
||||
try {
|
||||
const config = await client.query(api.configuracaoRelogio.obterConfiguracao, {});
|
||||
// Usar gmtOffset da configuração, sem valor padrão, pois 0 é um valor válido
|
||||
// Se não estiver configurado, usar null e tratar como 0
|
||||
const gmtOffset = config.gmtOffset ?? 0;
|
||||
|
||||
let timestampBase: number;
|
||||
@@ -45,16 +47,25 @@
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Usar tempo do servidor Convex
|
||||
timestampBase = await obterTempoServidor(client);
|
||||
sincronizado = true;
|
||||
// Usar relógio do PC (sem sincronização com servidor)
|
||||
timestampBase = obterTempoPC();
|
||||
sincronizado = false;
|
||||
usandoServidorExterno = false;
|
||||
erro = null;
|
||||
erro = 'Usando relógio do PC';
|
||||
}
|
||||
|
||||
// Aplicar GMT offset ao timestamp
|
||||
// O timestamp está em UTC, adicionar o offset em horas
|
||||
const timestampAjustado = timestampBase + (gmtOffset * 60 * 60 * 1000);
|
||||
// Quando GMT é 0, usar timestamp UTC puro e deixar toLocaleTimeString() fazer a conversão automática
|
||||
// Quando GMT ≠ 0, aplicar offset configurado ao timestamp
|
||||
let timestampAjustado: number;
|
||||
if (gmtOffset !== 0) {
|
||||
// Aplicar offset configurado
|
||||
timestampAjustado = timestampBase + (gmtOffset * 60 * 60 * 1000);
|
||||
} else {
|
||||
// Quando GMT = 0, manter timestamp UTC puro
|
||||
// O toLocaleTimeString() converterá automaticamente para o timezone local do navegador
|
||||
timestampAjustado = timestampBase;
|
||||
}
|
||||
tempoAtual = new Date(timestampAjustado);
|
||||
} catch (error) {
|
||||
console.error('Erro ao obter tempo:', error);
|
||||
@@ -120,7 +131,7 @@
|
||||
<span class="text-warning">{erro}</span>
|
||||
{:else}
|
||||
<Clock class="h-4 w-4 text-base-content/50" />
|
||||
<span class="text-base-content/50">Sincronizando...</span>
|
||||
<span class="text-base-content/50">Usando relógio do PC</span>
|
||||
{/if}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user