fc / storage-manager.js
Althnayi's picture
Upload 56 files
cca0cf3 verified
Raw
History Blame Contribute Delete
26.1 kB
/**
* @fileoverview Encrypted Unified Storage Manager (IndexedDB + Storage Layer + SIS Seal Protection)
* @module storage/storage-manager
* @description مدير التخزين الموحد المشفر عبر IndexedDB مع طابور تسلسلي للكتابة وأختام أمان رقمية لمقاومة التلاعب.
*/
(global => {
'use strict';
const DB_NAME = global.FritreeAppConstants?.STORAGE_CONSTANTS?.DB_NAME || "9a8b7c6d5e4f3a2b1c0";
const DB_VERSION = global.FritreeAppConstants?.STORAGE_CONSTANTS?.DB_VERSION || 6;
const STORES = global.FritreeAppConstants?.STORAGE_CONSTANTS?.STORES || Object.freeze({
MEDIA: "t7a8b9c0d",
HISTORY: "q1e2f3a4b",
SYSTEM: "a5c6d7e8f"
});
const SECURE_SIS_KEYS = new Set(
global.FritreeAppConstants?.STORAGE_CONSTANTS?.SECURE_SIS_KEYS || [
"acc_userLevel",
"acc_userXP",
"acc_lifetimeFb",
"acc_lifetimeWa",
"acc_lifetimeTasks",
"local_previous_posts",
"wa_connected",
"wa_sent_today",
"acc_fbPoints",
"acc_waPoints",
"usdPoints",
"local_contacts_database",
"local_calendar_events_db",
"local_post_groups",
"scheduledCampaignsData"
]
);
const INTEGRITY_SEAL_SALT = global.FritreeAppConstants?.SECURITY_SALTS?.STORAGE_SEAL_SALT ||
"FritreeStorageIntegritySealSalt_V5_AEAD_AES256";
const HASH_KEY_SALT = global.FritreeAppConstants?.SECURITY_SALTS?.HASH_KEY_SALT ||
"FritreeStorageKeyObfuscationSalt_2026_StrictSHA256_AEADMasterLock";
let dbInstance = null;
let isDbInitializing = false;
const dbInitPromiseResolvers = [];
/**
* معالجة الأخطاء الصامتة
*/
function silentTrap(err) {
if (err && console && console.warn) {
console.warn("[Fritree Storage Core SafeTrap]", err);
}
return null;
}
/**
* طابور الكتابة المتسلسل غير المتزامن (Async Write Queue)
* يضمن تنفيذ عمليات الكتابة بالترتيب ومنع تضارب الـ IndexedDB Transactions
*/
class AsyncWriteQueue {
constructor() {
this.queue = [];
this.isProcessing = false;
}
enqueue(task) {
return new Promise((resolve, reject) => {
this.queue.push({ task, resolve, reject });
this.processNext();
});
}
async processNext() {
if (this.isProcessing || this.queue.length === 0) return;
this.isProcessing = true;
const { task, resolve, reject } = this.queue.shift();
try {
const result = await task();
resolve(result);
} catch (err) {
silentTrap(err);
resolve(false);
} finally {
this.isProcessing = false;
this.processNext();
}
}
}
const storageWriteQueue = new AsyncWriteQueue();
/**
* تهيئة وفتح قاعدة بيانات IndexedDB
*/
function initIndexedDB() {
if (dbInstance) return Promise.resolve(dbInstance);
if (isDbInitializing) {
return new Promise((resolve, reject) => {
dbInitPromiseResolvers.push({ resolve, reject });
});
}
isDbInitializing = true;
return new Promise((resolve, reject) => {
try {
const request = indexedDB.open(DB_NAME, DB_VERSION);
request.onupgradeneeded = event => {
try {
const db = event.target.result;
Object.values(STORES).forEach(storeName => {
if (!db.objectStoreNames.contains(storeName)) {
db.createObjectStore(storeName, { keyPath: "id" });
}
});
} catch (e) {
silentTrap(e);
}
};
request.onsuccess = event => {
dbInstance = event.target.result;
isDbInitializing = false;
resolve(dbInstance);
dbInitPromiseResolvers.forEach(r => r.resolve(dbInstance));
dbInitPromiseResolvers.length = 0;
};
request.onerror = event => {
isDbInitializing = false;
resolve(null);
dbInitPromiseResolvers.forEach(r => r.resolve(null));
dbInitPromiseResolvers.length = 0;
};
} catch (err) {
isDbInitializing = false;
resolve(null);
}
});
}
/**
* تحويل Base64 إلى Blob
*/
function base64ToBlob(base64Data, mimeType = 'image/jpeg') {
try {
if (!base64Data || typeof base64Data !== 'string') {
return new Blob([], { type: mimeType });
}
const parts = base64Data.split(",");
const rawB64 = parts.length > 1 ? parts[1] : parts[0];
const detectedMime = parts.length > 1 ? parts[0].split(":")[1].split(";")[0] : mimeType;
const byteCharacters = atob(rawB64);
const byteArrays = [];
for (let offset = 0; offset < byteCharacters.length; offset += 512) {
const slice = byteCharacters.slice(offset, offset + 512);
const byteNumbers = new Array(slice.length);
for (let i = 0; i < slice.length; i++) {
byteNumbers[i] = slice.charCodeAt(i);
}
const byteArray = new Uint8Array(byteNumbers);
byteArrays.push(byteArray);
}
return new Blob(byteArrays, { type: detectedMime });
} catch (e) {
silentTrap(e);
return new Blob([], { type: mimeType });
}
}
/**
* تحويل Blob إلى Base64
*/
async function blobToBase64(blob) {
try {
if (!(blob instanceof Blob)) return null;
if (typeof FileReader !== 'undefined') {
return new Promise((resolve) => {
const reader = new FileReader();
reader.onloadend = () => resolve(reader.result);
reader.onerror = () => resolve(null);
reader.readAsDataURL(blob);
});
}
const buffer = await blob.arrayBuffer();
const bytes = new Uint8Array(buffer);
let binary = '';
const len = bytes.byteLength;
const chunk = 8192;
for (let i = 0; i < len; i += chunk) {
const slice = bytes.subarray(i, i + chunk);
binary += String.fromCharCode.apply(null, slice);
}
return `data:${blob.type || 'image/jpeg'};base64,${btoa(binary)}`;
} catch (err) {
silentTrap(err);
return null;
}
}
/**
* تشويش وتعمية مفاتيح التخزين
*/
async function asyncHashKey(key) {
try {
if (global.FritreeCrypto && typeof global.FritreeCrypto.sha256 === 'function') {
const hashHex = await global.FritreeCrypto.sha256(String(key) + HASH_KEY_SALT);
return "fte_sh_v5_" + hashHex.substring(0, 32);
}
let hash = 0;
const input = String(key) + HASH_KEY_SALT;
for (let i = 0; i < input.length; i++) {
hash = ((hash << 5) - hash) + input.charCodeAt(i);
hash |= 0;
}
return "fte_sh_v5_" + Math.abs(hash).toString(16);
} catch (e) {
silentTrap(e);
return "fallback_hash_" + String(key).length;
}
}
function syncHashKey(key) {
try {
let hash = 0;
const input = String(key) + HASH_KEY_SALT;
for (let i = 0; i < input.length; i++) {
hash = ((hash << 5) - hash) + input.charCodeAt(i);
hash |= 0;
}
return "fte_sh_v5_" + Math.abs(hash).toString(16);
} catch (e) {
silentTrap(e);
return "fallback_sync_" + String(key).length;
}
}
/**
* كتابة سجل داخل IndexedDB
*/
async function writeIndexedDBEntry(storeName, key, encryptedValue) {
try {
const db = await initIndexedDB();
if (!db) return false;
const obfuscatedKey = await asyncHashKey(key);
return new Promise((resolve) => {
const transaction = db.transaction([storeName], "readwrite");
const store = transaction.objectStore(storeName);
const putRequest = store.put({ id: obfuscatedKey, rawKeyName: key, payload: encryptedValue, updated_at: Date.now() });
putRequest.onsuccess = () => resolve(true);
putRequest.onerror = () => resolve(false);
});
} catch (e) {
silentTrap(e);
return false;
}
}
/**
* قراءة سجل من IndexedDB
*/
async function readIndexedDBEntry(storeName, key) {
try {
const db = await initIndexedDB();
if (!db) return null;
const obfuscatedKey = await asyncHashKey(key);
return new Promise((resolve) => {
const transaction = db.transaction([storeName], "readonly");
const store = transaction.objectStore(storeName);
const getRequest = store.get(obfuscatedKey);
getRequest.onsuccess = e => {
const record = e.target.result;
resolve(record ? record.payload : null);
};
getRequest.onerror = () => resolve(null);
});
} catch (e) {
silentTrap(e);
return null;
}
}
/**
* حذف سجل من IndexedDB
*/
async function deleteIndexedDBEntry(storeName, key) {
try {
const db = await initIndexedDB();
if (!db) return false;
const obfuscatedKey = await asyncHashKey(key);
return new Promise((resolve) => {
const transaction = db.transaction([storeName], "readwrite");
const store = transaction.objectStore(storeName);
const deleteRequest = store.delete(obfuscatedKey);
deleteRequest.onsuccess = () => resolve(true);
deleteRequest.onerror = () => resolve(false);
});
} catch (e) {
silentTrap(e);
return false;
}
}
/**
* توجيه المفتاح لمخزن الـ IndexedDB المناسب
*/
function routeIndexedDBStore(key) {
try {
if (key === "campaignHistoryData") {
return STORES.HISTORY;
} else if (key === "local_previous_posts" || key.startsWith("media_") || key.startsWith("rot_media_") || key.startsWith("wa_composer_")) {
return STORES.MEDIA;
}
return STORES.SYSTEM;
} catch (e) {
return STORES.SYSTEM;
}
}
/**
* حساب فحص الأمان المتناظر لأختام SIS
*/
async function computeSecureSymmetricChecksum(key, data) {
try {
let stringData = "";
if (key === 'usdPoints') {
stringData = parseFloat(data || 0).toFixed(2);
} else if (typeof data === 'object' && data !== null) {
stringData = JSON.stringify(data);
} else {
stringData = String(data !== undefined && data !== null ? data : "");
}
const payloadString = `${key}:${stringData}:${INTEGRITY_SEAL_SALT}`;
if (global.FritreeCrypto && typeof global.FritreeCrypto.sha256 === 'function') {
return await global.FritreeCrypto.sha256(payloadString);
}
let hash = 0;
for (let i = 0; i < payloadString.length; i++) {
hash = ((hash << 5) - hash) + payloadString.charCodeAt(i);
hash |= 0;
}
return "fallback_sha256_" + Math.abs(hash).toString(16);
} catch (e) {
silentTrap(e);
return "hash_error";
}
}
async function writeSecondarySignatureAnchor(key, signature) {
try {
const obfuscatedSigKey = await asyncHashKey(`sis_anchor_sig_${key}`);
if (typeof chrome !== 'undefined' && chrome.storage && chrome.storage.local) {
const payload = {};
payload[obfuscatedSigKey] = signature;
return new Promise(res => chrome.storage.local.set(payload, () => res(true)));
} else {
localStorage.setItem(syncHashKey(`sis_anchor_sig_${key}`), signature);
return true;
}
} catch (e) {
silentTrap(e);
return false;
}
}
async function readSecondarySignatureAnchor(key) {
try {
const obfuscatedSigKey = await asyncHashKey(`sis_anchor_sig_${key}`);
if (typeof chrome !== 'undefined' && chrome.storage && chrome.storage.local) {
return new Promise(res => {
chrome.storage.local.get(obfuscatedSigKey, payload => {
res(payload ? payload[obfuscatedSigKey] : null);
});
});
} else {
return localStorage.getItem(syncHashKey(`sis_anchor_sig_${key}`)) || null;
}
} catch (e) {
silentTrap(e);
return null;
}
}
async function deleteSecondarySignatureAnchor(key) {
try {
const obfuscatedSigKey = await asyncHashKey(`sis_anchor_sig_${key}`);
if (typeof chrome !== 'undefined' && chrome.storage && chrome.storage.local) {
return new Promise(res => chrome.storage.local.remove(obfuscatedSigKey, () => res(true)));
} else {
localStorage.removeItem(syncHashKey(`sis_anchor_sig_${key}`));
return true;
}
} catch (e) {
silentTrap(e);
return false;
}
}
/**
* كائن إدارة التخزين الموحد النهائي (Unified Storage API)
*/
const UnifiedStorageManager = {
/**
* تخزين عنصر مشفر
* @param {string} key
* @param {*} value
* @returns {Promise<boolean>}
*/
set: function(key, value) {
return storageWriteQueue.enqueue(async () => {
try {
if (global.FritreeCrypto && typeof global.FritreeCrypto.validateData === 'function') {
if (!global.FritreeCrypto.validateData(value, key)) {
console.error("[Fritree Storage] Data validation failed for key:", key);
return false;
}
}
if (SECURE_SIS_KEYS.has(key)) {
const dynamicSig = await computeSecureSymmetricChecksum(key, value);
await writeIndexedDBEntry(STORES.SYSTEM, `local_sis_sig_${key}`, dynamicSig);
await writeSecondarySignatureAnchor(key, dynamicSig);
await writeIndexedDBEntry(STORES.SYSTEM, `local_sis_backup_${key}`, value);
}
if (value instanceof Blob) {
try {
const targetStore = STORES.MEDIA;
await writeIndexedDBEntry(targetStore, key, value);
return true;
} catch (dbErr) {
silentTrap(dbErr);
return false;
}
}
const obfuscatedKey = await asyncHashKey(key);
const encryptedValue = await global.FritreeCrypto.encryptAndSignPayload(value, key);
if (!encryptedValue) {
console.error("[Fritree Storage] Encryption failed for key:", key);
return false;
}
if (typeof chrome !== 'undefined' && chrome.storage && chrome.storage.local) {
const payload = {};
payload[obfuscatedKey] = encryptedValue;
await new Promise(resolve => chrome.storage.local.set(payload, resolve));
} else {
localStorage.setItem(obfuscatedKey, encryptedValue);
}
const targetStore = routeIndexedDBStore(key);
await writeIndexedDBEntry(targetStore, key, encryptedValue);
return true;
} catch (ex) {
silentTrap(ex);
return false;
}
});
},
/**
* جلب وفك تشفير عنصر مع التحقق من الأختام واستعادة النسخ التلقائية
* @param {string} key
* @param {*} defaultValue
* @returns {Promise<*>}
*/
get: function(key, defaultValue = null) {
return new Promise(async resolve => {
try {
let decryptedValue = null;
let cipherPacketFound = false;
const obfuscatedKey = await asyncHashKey(key);
let cipherPacket = null;
if (typeof chrome !== 'undefined' && chrome.storage && chrome.storage.local) {
cipherPacket = await new Promise(r => {
chrome.storage.local.get(obfuscatedKey, result => {
r(result ? result[obfuscatedKey] : null);
});
});
} else {
cipherPacket = localStorage.getItem(obfuscatedKey);
}
if (cipherPacket !== null && cipherPacket !== undefined) {
cipherPacketFound = true;
} else {
const targetStore = routeIndexedDBStore(key);
cipherPacket = await readIndexedDBEntry(targetStore, key);
if (cipherPacket !== null && cipherPacket !== undefined) {
cipherPacketFound = true;
}
}
if (cipherPacket instanceof Blob) {
resolve(cipherPacket);
return;
}
if (cipherPacketFound && typeof cipherPacket === 'string') {
decryptedValue = await global.FritreeCrypto.verifyAndDecryptPayload(cipherPacket, key);
}
// تدقيق أختام الحماية (SIS Seals)
if (SECURE_SIS_KEYS.has(key)) {
if (decryptedValue === null) {
if (cipherPacketFound) {
console.warn("[Fritree Storage] Primary storage corrupted for key:", key, "- Restoring uncorrupted backup...");
}
const restoredBackup = await readIndexedDBEntry(STORES.SYSTEM, `local_sis_backup_${key}`);
if (restoredBackup !== null) {
decryptedValue = restoredBackup;
const restoredSig = await computeSecureSymmetricChecksum(key, restoredBackup);
await writeIndexedDBEntry(STORES.SYSTEM, `local_sis_sig_${key}`, restoredSig);
await writeSecondarySignatureAnchor(key, restoredSig);
await this.set(key, restoredBackup);
}
} else {
const actualComputedSig = await computeSecureSymmetricChecksum(key, decryptedValue);
const savedAnchorASig = await readIndexedDBEntry(STORES.SYSTEM, `local_sis_sig_${key}`);
const savedAnchorBSig = await readSecondarySignatureAnchor(key);
if (!savedAnchorASig && !savedAnchorBSig) {
await writeIndexedDBEntry(STORES.SYSTEM, `local_sis_sig_${key}`, actualComputedSig);
await writeSecondarySignatureAnchor(key, actualComputedSig);
await writeIndexedDBEntry(STORES.SYSTEM, `local_sis_backup_${key}`, decryptedValue);
} else if (savedAnchorASig && savedAnchorBSig) {
const isAValid = savedAnchorASig === actualComputedSig;
const isBValid = savedAnchorBSig === actualComputedSig;
if (!isAValid || !isBValid) {
console.error("[Fritree Storage] TAMPER DETECTED via SIS Seals for key:", key, "- Restoring uncorrupted backup.");
const restoredValue = await readIndexedDBEntry(STORES.SYSTEM, `local_sis_backup_${key}`);
if (restoredValue !== null) {
decryptedValue = restoredValue;
const restoredSig = await computeSecureSymmetricChecksum(key, restoredValue);
await writeIndexedDBEntry(STORES.SYSTEM, `local_sis_sig_${key}`, restoredSig);
await writeSecondarySignatureAnchor(key, restoredSig);
await this.set(key, restoredValue);
}
}
}
}
}
resolve(decryptedValue !== null ? decryptedValue : defaultValue);
} catch (e) {
silentTrap(e);
resolve(defaultValue);
}
});
},
/**
* حذف عنصر مشفر
*/
remove: function(key) {
return storageWriteQueue.enqueue(async () => {
try {
const obfuscatedKey = await asyncHashKey(key);
localStorage.removeItem(syncHashKey(key));
localStorage.removeItem(obfuscatedKey);
if (typeof chrome !== 'undefined' && chrome.storage && chrome.storage.local) {
chrome.storage.local.remove(obfuscatedKey);
}
const targetStore = routeIndexedDBStore(key);
await deleteIndexedDBEntry(targetStore, key);
if (SECURE_SIS_KEYS.has(key)) {
await deleteIndexedDBEntry(STORES.SYSTEM, `local_sis_sig_${key}`);
await deleteSecondarySignatureAnchor(key);
await deleteIndexedDBEntry(STORES.SYSTEM, `local_sis_backup_${key}`);
}
return true;
} catch (e) {
silentTrap(e);
return false;
}
});
},
/**
* مسح كامل قاعدة البيانات
*/
clear: function() {
return storageWriteQueue.enqueue(async () => {
try {
localStorage.clear();
if (typeof chrome !== 'undefined' && chrome.storage && chrome.storage.local) {
await new Promise(r => chrome.storage.local.clear(r));
}
if (dbInstance) {
dbInstance.close();
dbInstance = null;
}
return new Promise(resolve => {
const deleteReq = indexedDB.deleteDatabase(DB_NAME);
deleteReq.onsuccess = () => resolve(true);
deleteReq.onerror = () => resolve(false);
});
} catch (e) {
silentTrap(e);
return false;
}
});
},
b64ToBlob: base64ToBlob,
blobToB64: blobToBase64
};
Object.freeze(UnifiedStorageManager);
Object.defineProperty(global, 'FritreeStorage', {
value: UnifiedStorageManager,
writable: false,
configurable: false
});
initIndexedDB().catch(err => silentTrap(err));
})(typeof globalThis !== 'undefined' ? globalThis : typeof window !== 'undefined' ? window : this);