/** * BackupManager - Gerenciador de Backup e Restauração * Responsável por criar, gerenciar e restaurar backups do sistema */ class BackupManager { constructor() { this.backupKey = 'acoCalcPro_backups'; this.maxBackups = 5; this.autoBackupKey = 'acoCalcPro_auto_backup'; this.version = '1.0.0'; // Inicializar this.initialize(); } /** * Inicializa o gerenciador de backup */ initialize() { try { // Limpar backups antigos se necessário this.cleanupOldBackups(); console.log('🔄 BackupManager inicializado com sucesso'); } catch (error) { console.error('❌ Erro ao inicializar BackupManager:', error); } } /** * Cria um backup completo do sistema * @param {string} description - Descrição opcional do backup * @returns {Object} Backup criado */ async createBackup(description = 'Backup manual') { try { console.log('📦 Criando backup do sistema...'); // Coletar todos os dados necessários const now = Date.now(); const backupData = { id: `bkp-${now}`, timestamp: Number(now), createdAt: Number(now), type: description && description.toLowerCase().includes('auto') ? 'automatic' : 'manual', version: this.version, description: description, data: { // Configurações administrativas adminConfig: window.adminConfigManager ? window.adminConfigManager.getConfig() : null, // Preferências do usuário userPreferences: this.getUserPreferences(), // Cache stats do DataManager cacheStats: window.dataManager ? window.dataManager.getCacheStats() : null, // Estado da aplicação appState: this.getAppState(), // Dados do State Manager stateData: window.stateManager ? window.stateManager.getAllState() : null, // Dados do Cache Manager cacheData: window.cacheManager ? await this.getCacheData() : null } }; const sizeBytes = Number(JSON.stringify(backupData).length); backupData.sizeBytes = sizeBytes; backupData.size = this.formatBytes(sizeBytes); // Obter backups existentes const backups = this.getBackups(); // Adicionar novo backup no início backups.unshift(backupData); // Limitar número de backups if (backups.length > this.maxBackups) { const removed = backups.splice(this.maxBackups); console.log(`🗑️ ${removed.length} backup(s) antigo(s) removido(s)`); } // Salvar no localStorage localStorage.setItem(this.backupKey, JSON.stringify(backups)); // Atualizar último backup nas configurações if (window.adminConfigManager) { window.adminConfigManager.updateConfig('lastBackup', backupData.timestamp); } console.log('✅ Backup criado com sucesso:', new Date(backupData.timestamp).toLocaleString()); // Notificar sucesso if (window.toastManager) { window.toastManager.success('Backup criado com sucesso!'); } return backupData; } catch (error) { console.error('❌ Erro ao criar backup:', error); // Notificar erro if (window.toastManager) { window.toastManager.error('Erro ao criar backup: ' + error.message); } throw new Error('Falha ao criar backup: ' + error.message); } } /** * Obtém todos os backups salvos * @returns {Array} Lista de backups */ getBackups() { try { const saved = localStorage.getItem(this.backupKey); if (saved) { const raw = JSON.parse(saved); if (Array.isArray(raw)) { const normalized = raw.map(b => { const tsRaw = (b && b.timestamp != null) ? b.timestamp : Date.now(); let ts; if (typeof tsRaw === 'number') { ts = tsRaw; } else if (typeof tsRaw === 'string') { const parsed = Date.parse(tsRaw); ts = isNaN(parsed) ? Number(tsRaw) : parsed; } else { ts = Date.now(); } if (isNaN(ts)) ts = Date.now(); const id = (b && b.id) ? b.id : `bkp-${ts}`; const createdAtRaw = (b && b.createdAt != null) ? b.createdAt : ts; let createdAt; if (typeof createdAtRaw === 'number') { createdAt = createdAtRaw; } else if (typeof createdAtRaw === 'string') { const parsedCA = Date.parse(createdAtRaw); createdAt = isNaN(parsedCA) ? Number(createdAtRaw) : parsedCA; } else { createdAt = ts; } if (isNaN(createdAt)) createdAt = ts; const type = b.type || 'manual'; const version = b.version || this.version; const description = b.description || ''; const data = b.data || {}; let sizeBytes = Number(b.sizeBytes); if (isNaN(sizeBytes) || !sizeBytes) { sizeBytes = JSON.stringify(b).length; } const size = b.size || this.formatBytes(sizeBytes); return { id, timestamp: ts, createdAt, type, version, description, data, sizeBytes, size }; }); return normalized.filter(backup => this.isValidBackup(backup)); } } } catch (error) { console.warn('⚠️ Erro ao carregar backups:', error); } return []; } /** * Restaura um backup específico * @param {Object|string} backup - Backup a restaurar ou timestamp * @returns {boolean} Sucesso da restauração */ async restoreBackup(backup) { try { let backupToRestore; // Se for timestamp, encontrar o backup correspondente if (typeof backup === 'string' || typeof backup === 'number') { const backups = this.getBackups(); if (typeof backup === 'string' && isNaN(parseInt(backup))) { backupToRestore = backups.find(b => b.id === backup); } else { const timestamp = typeof backup === 'string' ? parseInt(backup) : backup; backupToRestore = backups.find(b => b.timestamp === timestamp); } if (!backupToRestore) { throw new Error('Backup não encontrado'); } } else { backupToRestore = backup; } console.log('📤 Restaurando backup de:', new Date(backupToRestore.createdAt || backupToRestore.timestamp).toLocaleString()); // Confirmar com usuário se for restauração manual const isConfirmed = await this.confirmRestoration(backupToRestore); if (!isConfirmed) { console.log('❌ Restauração cancelada pelo usuário'); return false; } // Criar backup antes de restaurar (precaução) await this.createBackup('Backup pré-restauração'); // Restaurar configurações administrativas if (backupToRestore.data.adminConfig && window.adminConfigManager) { window.adminConfigManager.saveConfig(backupToRestore.data.adminConfig); console.log('✅ Configurações administrativas restauradas'); } // Restaurar preferências do usuário if (backupToRestore.data.userPreferences) { this.setUserPreferences(backupToRestore.data.userPreferences); console.log('✅ Preferências do usuário restauradas'); } // Restaurar estado da aplicação if (backupToRestore.data.appState) { this.setAppState(backupToRestore.data.appState); console.log('✅ Estado da aplicação restaurado'); } // Restaurar dados do State Manager if (backupToRestore.data.stateData && window.stateManager) { window.stateManager.setAllState(backupToRestore.data.stateData); console.log('✅ Dados do State Manager restaurados'); } // Notificar sucesso if (window.toastManager) { window.toastManager.success('Backup restaurado com sucesso!'); } console.log('✅ Backup restaurado com sucesso'); // Disparar evento de restauração this.notifyRestorationComplete(backupToRestore); return true; } catch (error) { console.error('❌ Erro ao restaurar backup:', error); // Notificar erro if (window.toastManager) { window.toastManager.error('Erro ao restaurar backup: ' + error.message); } throw new Error('Falha ao restaurar backup: ' + error.message); } } /** * Remove um backup específico * @param {string|number} timestamp - Timestamp do backup a remover * @returns {boolean} Sucesso da remoção */ removeBackup(idOrTimestamp) { try { const backups = this.getBackups(); let filteredBackups; if (typeof idOrTimestamp === 'string' && isNaN(parseInt(idOrTimestamp))) { filteredBackups = backups.filter(backup => backup.id !== idOrTimestamp); } else { const timestampNum = typeof idOrTimestamp === 'string' ? parseInt(idOrTimestamp) : idOrTimestamp; filteredBackups = backups.filter(backup => backup.timestamp !== timestampNum); } if (filteredBackups.length === backups.length) { throw new Error('Backup não encontrado'); } localStorage.setItem(this.backupKey, JSON.stringify(filteredBackups)); console.log('🗑️ Backup removido'); if (window.toastManager) { window.toastManager.info('Backup removido com sucesso'); } return true; } catch (error) { console.error('❌ Erro ao remover backup:', error); throw new Error('Falha ao remover backup: ' + error.message); } } /** * Limpa todos os backups * @returns {boolean} Sucesso da limpeza */ clearAllBackups() { try { localStorage.removeItem(this.backupKey); console.log('🗑️ Todos os backups foram removidos'); if (window.toastManager) { window.toastManager.info('Todos os backups foram removidos'); } return true; } catch (error) { console.error('❌ Erro ao limpar backups:', error); throw new Error('Falha ao limpar backups: ' + error.message); } } /** * Exporta um backup como arquivo JSON * @param {Object|string} backup - Backup a exportar ou timestamp */ exportBackup(backup) { try { let backupToExport; // Se for timestamp, encontrar o backup correspondente if (typeof backup === 'string' || typeof backup === 'number') { const timestamp = typeof backup === 'string' ? parseInt(backup) : backup; const backups = this.getBackups(); backupToExport = backups.find(b => b.timestamp === timestamp); if (!backupToExport) { throw new Error('Backup não encontrado'); } } else { backupToExport = backup; } const jsonString = JSON.stringify(backupToExport, null, 2); const blob = new Blob([jsonString], { type: 'application/json' }); const url = URL.createObjectURL(blob); const a = document.createElement('a'); a.href = url; a.download = `steelbase-backup-${backupToExport.timestamp}.json`; document.body.appendChild(a); a.click(); document.body.removeChild(a); URL.revokeObjectURL(url); console.log('📤 Backup exportado com sucesso'); if (window.toastManager) { window.toastManager.success('Backup exportado com sucesso!'); } } catch (error) { console.error('❌ Erro ao exportar backup:', error); if (window.toastManager) { window.toastManager.error('Erro ao exportar backup: ' + error.message); } throw new Error('Falha ao exportar backup: ' + error.message); } } /** * Importa um backup de arquivo JSON * @param {File} file - Arquivo JSON a importar * @returns {Object} Backup importado */ async importBackup(file) { try { console.log('📥 Importando backup de arquivo...'); const text = await file.text(); const backup = JSON.parse(text); if (!this.isValidBackup(backup)) { throw new Error('Arquivo de backup inválido'); } // Adicionar aos backups existentes const backups = this.getBackups(); backups.unshift(backup); // Limitar número de backups if (backups.length > this.maxBackups) { backups.splice(this.maxBackups); } localStorage.setItem(this.backupKey, JSON.stringify(backups)); console.log('✅ Backup importado com sucesso'); if (window.toastManager) { window.toastManager.success('Backup importado com sucesso!'); } return backup; } catch (error) { console.error('❌ Erro ao importar backup:', error); if (window.toastManager) { window.toastManager.error('Erro ao importar backup: ' + error.message); } throw new Error('Falha ao importar backup: ' + error.message); } } /** * Configura backup automático * @param {boolean} enabled - Se deve estar ativado * @param {number} intervalHours - Intervalo em horas */ setupAutoBackup(enabled, intervalHours = 24) { try { const autoBackupConfig = { enabled: enabled, intervalHours: intervalHours, lastBackup: null }; localStorage.setItem(this.autoBackupKey, JSON.stringify(autoBackupConfig)); if (enabled) { console.log(`🔄 Backup automático configurado: a cada ${intervalHours} horas`); this.startAutoBackupTimer(); } else { console.log('⏹️ Backup automático desativado'); this.stopAutoBackupTimer(); } } catch (error) { console.error('❌ Erro ao configurar backup automático:', error); } } /** * Inicia o timer de backup automático */ startAutoBackupTimer() { // Implementar timer de backup automático // Isso seria chamado na inicialização da aplicação console.log('⏰ Timer de backup automático iniciado'); } /** * Para o timer de backup automático */ stopAutoBackupTimer() { console.log('⏹️ Timer de backup automático parado'); } /** * Valida se um backup é válido * @param {Object} backup - Backup a validar * @returns {boolean} Se é válido */ isValidBackup(backup) { if (!backup || typeof backup !== 'object') { return false; } // Verificar campos obrigatórios const requiredFields = ['timestamp', 'version', 'data']; for (const field of requiredFields) { if (!backup[field]) { console.warn(`⚠️ Campo obrigatório ausente no backup: ${field}`); return false; } } // Verificar estrutura de dados if (typeof backup.timestamp !== 'number' || typeof backup.version !== 'string' || typeof backup.data !== 'object') { return false; } return true; } /** * Limpa backups antigos (mantém apenas os mais recentes) */ cleanupOldBackups() { try { const backups = this.getBackups(); if (backups.length > this.maxBackups) { const keptBackups = backups.slice(0, this.maxBackups); localStorage.setItem(this.backupKey, JSON.stringify(keptBackups)); console.log(`🗑️ ${backups.length - keptBackups.length} backup(s) antigo(s) removido(s)`); } } catch (error) { console.warn('⚠️ Erro ao limpar backups antigos:', error); } } /** * Obtém preferências do usuário * @returns {Object} Preferências */ getUserPreferences() { try { const saved = localStorage.getItem('acoCalcPreferences'); return saved ? JSON.parse(saved) : null; } catch (error) { console.warn('⚠️ Erro ao obter preferências do usuário:', error); return null; } } /** * Define preferências do usuário * @param {Object} preferences - Preferências a definir */ setUserPreferences(preferences) { try { localStorage.setItem('acoCalcPreferences', JSON.stringify(preferences)); } catch (error) { console.warn('⚠️ Erro ao definir preferências do usuário:', error); } } /** * Obtém estado da aplicação * @returns {Object} Estado da aplicação */ getAppState() { try { // Coletar estado relevante da aplicação const state = { currentSection: window.currentSection || null, lastUpdate: window.lastUpdate || null, userSession: window.userSession || null }; return state; } catch (error) { console.warn('⚠️ Erro ao obter estado da aplicação:', error); return {}; } } /** * Define estado da aplicação * @param {Object} state - Estado a definir */ setAppState(state) { try { if (state.currentSection) window.currentSection = state.currentSection; if (state.lastUpdate) window.lastUpdate = state.lastUpdate; if (state.userSession) window.userSession = state.userSession; } catch (error) { console.warn('⚠️ Erro ao definir estado da aplicação:', error); } } /** * Obtém dados do cache * @returns {Promise} Dados do cache */ async getCacheData() { try { if (!window.cacheManager) { return null; } // Obter dados relevantes do cache const cacheData = { metadata: window.cacheManager.getMetadata ? window.cacheManager.getMetadata() : null, stats: window.cacheManager.getStats ? window.cacheManager.getStats() : null }; return cacheData; } catch (error) { console.warn('⚠️ Erro ao obter dados do cache:', error); return null; } } /** * Confirma restauração com o usuário * @param {Object} backup - Backup a restaurar * @returns {Promise} Confirmação */ async confirmRestoration(backup) { // Em ambiente real, isso seria um modal/dialog // Por enquanto, retorna true para testes return new Promise((resolve) => { console.log(`🔄 Confirmando restauração do backup: ${new Date(backup.timestamp).toLocaleString()}`); // Simular confirmação após 1 segundo setTimeout(() => { resolve(true); }, 1000); }); } /** * Notifica conclusão da restauração * @param {Object} backup - Backup restaurado */ notifyRestorationComplete(backup) { const event = new CustomEvent('backupRestored', { detail: { backup: backup }, bubbles: true }); document.dispatchEvent(event); } /** * Obtém estatísticas dos backups * @returns {Object} Estatísticas */ getStats() { const backups = this.getBackups(); const totalSize = backups.reduce((total, backup) => { return total + JSON.stringify(backup).length; }, 0); return { totalBackups: backups.length, oldestBackup: backups.length > 0 ? backups[backups.length - 1].timestamp : null, newestBackup: backups.length > 0 ? backups[0].timestamp : null, totalSize: totalSize, maxBackups: this.maxBackups }; } /** * Retorna um backup específico por id ou timestamp * @param {string|number} idOrTimestamp * @returns {Object|null} */ getBackup(idOrTimestamp) { const backups = this.getBackups(); if (typeof idOrTimestamp === 'string' && isNaN(parseInt(idOrTimestamp))) { return backups.find(b => b.id === idOrTimestamp) || null; } const ts = typeof idOrTimestamp === 'string' ? Number(parseInt(idOrTimestamp)) : Number(idOrTimestamp); return backups.find(b => b.timestamp === ts) || null; } /** * Formata bytes em string legível (KB, MB, GB) * @param {number} bytes * @returns {string} */ formatBytes(bytes) { if (!bytes || bytes < 1024) return `${bytes || 0} B`; const units = ['KB', 'MB', 'GB']; let value = bytes / 1024; let idx = 0; while (value >= 1024 && idx < units.length - 1) { value /= 1024; idx++; } return `${value.toFixed(1)} ${units[idx]}`; } } // Criar instância global window.backupManager = new BackupManager(); console.log('🔄 BackupManager inicializado com sucesso');