File size: 18,065 Bytes
2a196ac
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
// ============================================================================
// 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, '&lt;')
            .replace(/>/g, '&gt;')
            .replace(/"/g, '&quot;')
            .replace(/'/g, '&#x27;');
    }

    // ============================================================================
    // 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);