// ============================================================================ // File: modules/migration.js // ============================================================================ (global => { 'use strict'; /** * Sanitizes data strings to protect DOM from XSS */ function sanitizeDataString(str) { if (!str) return ''; return String(str) .replace(/&/g, '&') .replace(//g, '>') .replace(/"/g, '"') .replace(/'/g, '''); } // ============================================================================ // Cryptographic Migration Module Initializer // ============================================================================ function initMigrationModule() { try { const selectAllCheck = document.getElementById('chk-ie-all'); const exportBtn = document.getElementById('btn-export-settings'); const importTriggerBtn = document.getElementById('btn-import-settings-trigger'); const fileInput = document.getElementById('file-import-settings'); if (selectAllCheck) { selectAllCheck.addEventListener('change', (e) => { const isChecked = e.target.checked; const ids = ['chk-ie-cl', 'chk-ie-ch', 'chk-ie-sch', 'chk-ie-grp', 'chk-ie-int']; ids.forEach(id => { const el = document.getElementById(id); if (el) el.checked = isChecked; }); }); } if (exportBtn) exportBtn.addEventListener('click', exportSystemBackup); if (importTriggerBtn) { importTriggerBtn.addEventListener('click', () => { if (fileInput) fileInput.click(); }); } if (fileInput) fileInput.addEventListener('change', handleImportSettingsFile); if (typeof window.addLog === 'function') { window.addLog('Workspace backup, restore, and cryptographic migration modules successfully initialized.', 'info'); } } catch (e) { console.error("[Migration Core] Failed to initialize migration modules:", e); } } // ============================================================================ // Workspace Backup Exporter (Strict AES-256-GCM) // ============================================================================ async function exportSystemBackup() { const msgContainer = document.getElementById('ie-status-msg'); if (msgContainer) msgContainer.textContent = "Packaging, compressing, and encrypting active workspace data blocks..."; const passphrase = prompt("Enter a secure backup protection password (minimum 8 characters):"); if (!passphrase || passphrase.length < 8) { alert("Export aborted: Password must be at least 8 characters to ensure standard encryption strength."); if (msgContainer) msgContainer.textContent = "Export cancelled by user."; return; } const targetAccountId = prompt("If you wish to restrict restoration to a specific account, enter their 128-character public key (or leave blank for public use):"); if (targetAccountId && targetAccountId.trim().length !== 128) { alert("Export aborted: The target public key must be exactly 128 alphanumeric characters."); if (msgContainer) msgContainer.textContent = "Export failed due to invalid destination lock parameters."; return; } try { const exportPayload = { timestamp: new Date().toISOString(), version: "1.2.0-Simultaneous", algo: "PBKDF2-SHA256-AES-GCM" }; // 1. Pack Rotation matrix and templates in their current form if (document.getElementById('chk-ie-cl')?.checked) { exportPayload.contentLibraryData = await FritreeStorage.get('local_content_library', []); exportPayload.contentCategoriesData = await FritreeStorage.get('local_content_categories', []); exportPayload.previousPostsData = await FritreeStorage.get('local_previous_posts', []); exportPayload.selectedPrevPostIds = await FritreeStorage.get('local_selected_prev_posts', []); exportPayload.isRotationActive = await FritreeStorage.get('local_is_rotation_active', 'false'); exportPayload.rotationMode = await FritreeStorage.get('local_rotation_mode', 'balanced'); } // 2. Pack Campaign history ledger if (document.getElementById('chk-ie-ch')?.checked) { exportPayload.campaignHistoryData = await FritreeStorage.get('campaignHistoryData', []); } // 3. Pack queue and scheduled parameters if (document.getElementById('chk-ie-sch')?.checked) { exportPayload.scheduledCampaigns = await FritreeStorage.get('scheduledCampaignsData', []); } // 4. Pack Groups registry and segments if (document.getElementById('chk-ie-grp')?.checked) { exportPayload.savedTags = await FritreeStorage.get('local_saved_tags', {}); exportPayload.selectedGroupIds = await FritreeStorage.get('local_selected_groups', []); exportPayload.localGroups = await FritreeStorage.get('local_groups', []); } // 5. Pack Cooldowns and active protection configurations if (document.getElementById('chk-ie-int')?.checked) { exportPayload.sleepIntervals = await FritreeStorage.get('local_sleep_intervals', {}); exportPayload.protectionSettings = await FritreeStorage.get('local_protection_settings', {}); exportPayload.shieldConfigMatrix = await FritreeStorage.get('local_shield_config_matrix', {}); } // 6. Pack transaction records and verified keys (EXCLUDING userPoints and fritreeAccountId) exportPayload.pointsTransactions = await FritreeStorage.get('pointsTransactions', []); exportPayload.usedSerials = await FritreeStorage.get('usedSerials', []); exportPayload.usedTokens = await FritreeStorage.get('usedTokens', []); // 7. Pack XP levels, multipliers, and achievements stats (EXCLUDING acc_fbShares and acc_waShares) exportPayload.acc_userXP = await FritreeStorage.get('acc_userXP', 0); exportPayload.acc_userLevel = await FritreeStorage.get('acc_userLevel', 1); exportPayload.acc_lifetimeFb = await FritreeStorage.get('acc_lifetimeFb', 0); exportPayload.acc_lifetimeWa = await FritreeStorage.get('acc_lifetimeWa', 0); exportPayload.acc_lifetimeTasks = await FritreeStorage.get('acc_lifetimeTasks', 0); exportPayload.acc_activeSub = await FritreeStorage.get('acc_activeSub', 'Lifetime Unlimited'); exportPayload.acc_subExpiry = await FritreeStorage.get('acc_subExpiry', null); exportPayload.acc_tasks = await FritreeStorage.get('acc_tasks', []); // 8. Pack WhatsApp broadcast environment variables exportPayload.wa_connected = await FritreeStorage.get('wa_connected', false); exportPayload.wa_phone_number = await FritreeStorage.get('wa_phone_number', 'Online'); exportPayload.wa_profile_name = await FritreeStorage.get('wa_profile_name', 'Active Account'); exportPayload.wa_sent_today = await FritreeStorage.get('wa_sent_today', 0); const plainText = JSON.stringify(exportPayload); const targetId = targetAccountId ? targetAccountId.trim() : null; // Trigger AES-256-GCM encryption with destination account lock logic const encryptedBase64 = await FritreeCrypto.encryptBackupString(plainText, passphrase, targetId); if (encryptedBase64) { const blob = new Blob([encryptedBase64], { type: 'application/octet-stream' }); const url = URL.createObjectURL(blob); const a = document.createElement('a'); a.href = url; // Formulate target-specific seconds-level filename const now = new Date(); const pad = num => String(num).padStart(2, '0'); const timestamp = `${now.getFullYear()}-${pad(now.getMonth() + 1)}-${pad(now.getDate())}_${pad(now.getHours())}-${pad(now.getMinutes())}-${pad(now.getSeconds())}`; const fileSuffix = targetId ? "_RESTRICTED" : "_GENERAL"; a.download = `Fritree_Backup_${fileSuffix}_${timestamp}.txt`; a.click(); URL.revokeObjectURL(url); if (msgContainer) msgContainer.textContent = "Workspace backup package (.txt) generated and downloaded successfully."; if (typeof window.addLog === 'function') { window.addLog(`Encrypted backup of complete workspace state compiled successfully (${targetId ? "restricted destination lock applied" : "general public unlock allowed"}).`, 'success'); } } } catch (e) { console.error("[Migration Core] Failed to compile backup package:", e); if (msgContainer) msgContainer.textContent = "An error occurred during encryption packaging processing."; } } // ============================================================================ // Workspace Backup Importer & Environment Restorer // ============================================================================ async function handleImportSettingsFile(event) { const file = event.target.files[0]; if (!file) return; const msgContainer = document.getElementById('ie-status-msg'); if (msgContainer) msgContainer.textContent = "Reading backup file and verifying cryptographic signatures..."; const passphrase = prompt("Enter the protection password associated with this backup file to restore workspace state:"); if (!passphrase) { if (msgContainer) msgContainer.textContent = "Import cancelled by user."; event.target.value = ''; return; } const reader = new FileReader(); reader.onload = async (ev) => { try { const encryptedText = ev.target.result; const currentId = await FritreeCrypto.getOrGenerateAccountId(); // Supply active local account ID to verify restriction boundaries on decryption const decryptedText = await FritreeCrypto.decryptBackupString(encryptedText, passphrase, currentId); if (!decryptedText) { alert('Import failed: Decryption error. The password is incorrect, or the backup file has been corrupted or modified.'); if (msgContainer) msgContainer.textContent = "Decryption failure during restore process."; event.target.value = ''; return; } const data = JSON.parse(decryptedText); // Write decrypted database records securely using indexedDB if (data.contentLibraryData) await FritreeStorage.set('local_content_library', data.contentLibraryData); if (data.contentCategoriesData) await FritreeStorage.set('local_content_categories', data.contentCategoriesData); if (data.campaignHistoryData) await FritreeStorage.set('campaignHistoryData', data.campaignHistoryData); if (data.scheduledCampaigns) await FritreeStorage.set('scheduledCampaignsData', data.scheduledCampaigns); if (data.savedTags) await FritreeStorage.set('local_saved_tags', data.savedTags); if (data.selectedGroupIds) await FritreeStorage.set('local_selected_groups', data.selectedGroupIds); if (data.localGroups) await FritreeStorage.set('local_groups', data.localGroups); if (data.sleepIntervals) await FritreeStorage.set('local_sleep_intervals', data.sleepIntervals); if (data.protectionSettings) await FritreeStorage.set('local_protection_settings', data.protectionSettings); if (data.shieldConfigMatrix) await FritreeStorage.set('local_shield_config_matrix', data.shieldConfigMatrix); // Restore Rotation templates and state in its current form if (data.previousPostsData) { await FritreeStorage.set('local_previous_posts', data.previousPostsData); // Recalculate local rotation matrix integrity signature const STATE_INTEGRITY_SALT = "FritreeRotationMatrixDynamicIntegrityVerificationSalt_2026"; const structuralConcat = data.previousPostsData.map(p => `${p.id}:${p.status}`).sort().join('||'); let computedSignature = "lite_hash_" + structuralConcat.length; if (typeof FritreeCrypto !== 'undefined' && typeof FritreeCrypto.signReceipt === 'function') { computedSignature = await FritreeCrypto.signReceipt("ROT_MATRIX", structuralConcat.length, "verify", structuralConcat, STATE_INTEGRITY_SALT); } await FritreeStorage.set('local_rotation_matrix_integrity_sig', computedSignature); } if (data.selectedPrevPostIds) await FritreeStorage.set('local_selected_prev_posts', data.selectedPrevPostIds); if (data.isRotationActive) await FritreeStorage.set('local_is_rotation_active', data.isRotationActive); if (data.rotationMode) await FritreeStorage.set('local_rotation_mode', data.rotationMode); // Restore points transaction matrices (ignoring userPoints and fritree_account_id to prevent browser modifications) if (data.pointsTransactions) await FritreeStorage.set('pointsTransactions', data.pointsTransactions); if (data.usedSerials) await FritreeStorage.set('usedSerials', data.usedSerials); if (data.usedTokens) await FritreeStorage.set('usedTokens', data.usedTokens); // Restore level statistics & XP progress indicators (ignoring acc_fbShares and acc_waShares) if (data.acc_userXP !== undefined) await FritreeStorage.set('acc_userXP', data.acc_userXP); if (data.acc_userLevel !== undefined) await FritreeStorage.set('acc_userLevel', data.acc_userLevel); if (data.acc_lifetimeFb !== undefined) await FritreeStorage.set('acc_lifetimeFb', data.acc_lifetimeFb); if (data.acc_lifetimeWa !== undefined) await FritreeStorage.set('acc_lifetimeWa', data.acc_lifetimeWa); if (data.acc_lifetimeTasks !== undefined) await FritreeStorage.set('acc_lifetimeTasks', data.acc_lifetimeTasks); if (data.acc_activeSub !== undefined) await FritreeStorage.set('acc_activeSub', data.acc_activeSub); if (data.acc_subExpiry !== undefined) await FritreeStorage.set('acc_subExpiry', data.acc_subExpiry); if (data.acc_tasks !== undefined) await FritreeStorage.set('acc_tasks', data.acc_tasks); // Restore WhatsApp broadcast environment variables if (data.wa_connected !== undefined) await FritreeStorage.set('wa_connected', data.wa_connected); if (data.wa_phone_number !== undefined) await FritreeStorage.set('wa_phone_number', data.wa_phone_number); if (data.wa_profile_name !== undefined) await FritreeStorage.set('wa_profile_name', data.wa_profile_name); if (data.wa_sent_today !== undefined) await FritreeStorage.set('wa_sent_today', data.wa_sent_today); if (msgContainer) msgContainer.textContent = "Workspace state imported and restored successfully! Reloading environment to apply changes..."; if (typeof window.addLog === 'function') { window.addLog('Verified backup imported successfully. Reloading active session environment.', 'success'); } setTimeout(() => { window.location.reload(); }, 1500); } catch (err) { console.error("[Migration Core] Error occurred during backup package importing:", err); let fallbackErrorMsg = 'Restore failed: Please ensure you select a valid .fritree backup file and enter the correct decryption password.'; if (err.message === "RESTRICTED_ACCESS_DENIED") { fallbackErrorMsg = "Decryption failure: This backup package was restricted for a different workspace ID."; } alert(fallbackErrorMsg); if (msgContainer) msgContainer.textContent = "Restore failed."; } event.target.value = ''; }; reader.readAsText(file); } // Exports global.FritreeMigration = { init: initMigrationModule, export: exportSystemBackup, import: handleImportSettingsFile }; if (document.readyState === 'complete' || document.readyState === 'interactive') { initMigrationModule(); } else { document.addEventListener('DOMContentLoaded', () => initMigrationModule()); } })(typeof globalThis !== 'undefined' ? globalThis : typeof window !== 'undefined' ? window : this);