facebook / migration.js
fritreefw's picture
Upload 22 files
3210117 verified
Raw History Blame Contribute Delete
20.5 kB
// ============================================================================
// File: migration.js
// ============================================================================
(global => {
'use strict';
/**
* Injects HTML structures dynamically
*/
function renderStructuralHTMLTemplates() {
const paneMigration = document.getElementById('pane-migration');
if (paneMigration) {
paneMigration.innerHTML = `
<div class="card">
<div class="card-title">
<span><i class="fa-solid fa-database"></i><span>Workspace System Backup &amp; State Migration</span></span>
</div>
<div class="export-grid" style="margin-top: 0;">
<div class="export-section">
<h3><i class="fa-solid fa-filter"></i><span>Choose data blocks to include in your package:</span></h3>
<div class="export-checkboxes">
<div class="checkbox-group"><input type="checkbox" id="chk-ie-cl" checked> <label><i class="fa-solid fa-arrows-spin"></i><span>Rotation Matrix &amp; Broadcast Templates</span></label></div>
<div class="checkbox-group"><input type="checkbox" id="chk-ie-ch" checked> <label><i class="fa-solid fa-box-archive"></i><span>Campaign History &amp; Delivery Ledgers</span></label></div>
<div class="checkbox-group"><input type="checkbox" id="chk-ie-sch" checked> <label><i class="fa-solid fa-calendar-day"></i><span>Scheduled Tasks &amp; Dispatch Queue</span></label></div>
<div class="checkbox-group"><input type="checkbox" id="chk-ie-grp" checked> <label><i class="fa-solid fa-users"></i><span>Groups Registry &amp; Saved Target Segments</span></label></div>
<div class="checkbox-group"><input type="checkbox" id="chk-ie-int" checked> <label><i class="fa-solid fa-user-secret"></i><span>Cooldown Parameters &amp; Stealth Profiles</span></label></div>
</div>
<div class="checkbox-group" style="margin-top:10px; border-top: 1px solid #e2e8f0; padding-top: 10px;">
<input type="checkbox" id="chk-ie-all" checked>
<label style="font-weight:bold; color:var(--primary);"><i class="fa-solid fa-folder-open"></i><span>Package All Local Database Store Blocks</span></label>
</div>
</div>
<!-- Import/Export Tools -->
<div class="export-section">
<h3><i class="fa-solid fa-shield-halved"></i><span>Secure Export / Import Console</span></h3>
<p style="font-size: 12px; color: var(--text-muted); margin-bottom: 15px;">
<i class="fa-solid fa-circle-info"></i><span>Export your workspace settings, targets, templates, and logs into a single encrypted backups file (.txt) to safely migrate your setup.</span>
</p>
<!-- Target Account restrictions -->
<div class="form-group" style="margin-bottom: 12px;">
<label style="font-size: 11px; font-weight: bold;"><i class="fa-solid fa-lock"></i><span>Restrict Backup file to a specific Account ID (Optional):</span></label>
<input type="text" id="export-restricted-target-id" class="search-box" style="font-family: monospace; font-size: 11px;" placeholder="Paste the target public key to restrict restoring on other systems...">
</div>
<div style="display: flex; gap: 10px; margin-bottom: 10px;">
<button id="btn-export-settings" class="btn-primary" style="flex: 1;"><i class="fa-solid fa-file-export"></i><span>Export Backup Package</span></button>
<button id="btn-import-settings-trigger" class="btn-secondary" style="flex: 1; background: #f59e0b; color: white;"><i class="fa-solid fa-file-import"></i><span>Import Backup Package</span></button>
<input type="file" id="file-import-settings" accept=".txt" style="display: none;">
</div>
<div id="ie-status-msg" style="font-size: 11px; color: var(--success); font-weight: bold; text-align: center;"></div>
</div>
</div>
</div>
`;
}
}
function sanitizeDataString(str) {
if (!str) return '';
return String(str)
.replace(/&/g, '&amp;')
.replace(/</g, '&lt;')
.replace(/>/g, '&gt;')
.replace(/"/g, '&quot;')
.replace(/'/g, '&#x27;');
}
function initMigrationModule() {
try {
renderStructuralHTMLTemplates();
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);
}
}
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"
};
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');
}
if (document.getElementById('chk-ie-ch')?.checked) {
exportPayload.campaignHistoryData = await FritreeStorage.get('campaignHistoryData', []);
}
if (document.getElementById('chk-ie-sch')?.checked) {
exportPayload.scheduledCampaigns = await FritreeStorage.get('scheduledCampaignsData', []);
}
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', []);
}
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', {});
}
exportPayload.pointsTransactions = await FritreeStorage.get('pointsTransactions', []);
exportPayload.usedSerials = await FritreeStorage.get('usedSerials', []);
exportPayload.usedTokens = await FritreeStorage.get('usedTokens', []);
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', []);
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;
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;
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.";
}
}
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();
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);
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);
if (data.previousPostsData) {
await FritreeStorage.set('local_previous_posts', data.previousPostsData);
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);
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);
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);
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 .txt 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);
}
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);