UNPKG

alphabase

Version:

AlphaBase V3.0 - Advanced file-based database with JWT authentication, RSA encryption, audit logging, HTTP server, and comprehensive security features.

1,143 lines (1,040 loc) 37.9 kB
// Multi-DB manager class AlphaBaseManager { constructor() { this.databases = {}; } open(filePath, options = {}) { if (!this.databases[filePath]) { this.databases[filePath] = new AlphaBase({ ...options, filePath }); } return this.databases[filePath]; } close(filePath) { delete this.databases[filePath]; } list() { return Object.keys(this.databases); } get(filePath) { return this.databases[filePath]; } } const fs = require('fs'); const path = require('path'); const Ajv = require('ajv'); const encryption = require('./encryption'); const PerformanceOptimizer = require('./performance'); const { ConnectionPool, FileLockManager } = require('./pool'); // Native Base64 encryption/decryption helpers function base64Encrypt(str) { return Buffer.from(str, 'utf8').toString('base64'); } function base64Decrypt(str) { return Buffer.from(str, 'base64').toString('utf8'); } const crypto = require('crypto'); const { AESEncryption, DESEncryption, TripleDESEncryption, RabbitEncryption, XOREncryption } = require('./encryption'); const { JWTAuth, AuditLogger, DataIntegrity, RSAEncryption } = require('./security'); // Global instances registry for cleanup const globalInstances = new Set(); // Cleanup function for all instances const cleanupAllInstances = async () => { const cleanupPromises = Array.from(globalInstances).map(instance => { if (instance && instance.cleanup) { return instance.cleanup().catch(() => {}); // Ignore cleanup errors } }); await Promise.all(cleanupPromises); globalInstances.clear(); }; // Handle process termination process.on('exit', () => cleanupAllInstances()); process.on('SIGINT', () => cleanupAllInstances().then(() => process.exit(0))); process.on('SIGTERM', () => cleanupAllInstances().then(() => process.exit(0))); /** * AlphaBase - A lightweight JSON database with encryption and advanced features * @version 3.0.0 */ class AlphaBase { // Synchronous: check if key exists hasSync(key) { if (typeof key !== 'string') throw new TypeError('Key must be a string'); this.cleanupSync(); if (this.ttlMeta[key] && this.ttlMeta[key] < Date.now()) { delete this.data[key]; delete this.ttlMeta[key]; this._saveSync(); return false; } return Object.prototype.hasOwnProperty.call(this.data, key); } // Synchronous: clear all data clearSync() { this.data = {}; this.ttlMeta = {}; this._saveSync(); } // Synchronous: get all data allSync() { this.cleanupSync(); const out = {}; for (const key of Object.keys(this.data)) { if (!this.ttlMeta[key] || this.ttlMeta[key] > Date.now()) { out[key] = this.data[key]; } } return out; } // Internal: add change to document history (avoid circular refs) _addHistory(doc, change) { if (!doc._history) doc._history = []; if (!doc.updatedAt) doc.updatedAt = Date.now(); // Avoid circular reference by not storing the full object in value let safeChange = { ...change, at: Date.now() }; if (safeChange.value && typeof safeChange.value === 'object') { try { safeChange.value = JSON.parse(JSON.stringify(safeChange.value)); } catch { safeChange.value = '[Unserializable]'; } } doc._history.unshift(safeChange); if (doc._history.length > 3) doc._history = doc._history.slice(0, 3); doc.updatedAt = Date.now(); } // Transaction support beginTransaction() { if (this._transaction) throw new Error('Transaction already in progress'); this._transaction = { data: JSON.parse(JSON.stringify(this.data)), ttlMeta: JSON.parse(JSON.stringify(this.ttlMeta)) }; } commit() { if (!this._transaction) throw new Error('No transaction in progress'); this._transaction = null; this._saveSync(); } rollback() { if (!this._transaction) throw new Error('No transaction in progress'); this.data = this._transaction.data; this.ttlMeta = this._transaction.ttlMeta; this._transaction = null; this._saveSync(); } transactionSync(ops) { this.beginTransaction(); try { this.batchSync(ops); this.commit(); } catch (e) { this.rollback(); throw e; } } // Scheduled cleanup startScheduledCleanup(intervalMs) { if (this._cleanupInterval) clearInterval(this._cleanupInterval); this._cleanupInterval = setInterval(() => { try { this.cleanupSync(); } catch (e) {} }, intervalMs); } stopScheduledCleanup() { if (this._cleanupInterval) clearInterval(this._cleanupInterval); this._cleanupInterval = null; } /** * Exports a specific collection to a JSON file. * @param {string} collection - Collection name (key) * @param {string} filePath - Output file path */ exportCollection(collection, filePath) { if (typeof collection !== 'string') throw new TypeError('Collection name must be a string'); if (!filePath || typeof filePath !== 'string') throw new TypeError('File path must be a string'); const data = this.data[collection]; if (!data) throw new Error(`Collection '${collection}' not found.`); let out = JSON.stringify(data, null, 2); // Optional encryption for export if (arguments[2] && arguments[2].encrypt && this.password) { let encType = this.encryptionType; let encrypted; if (encType === 'Base64') { encrypted = base64Encrypt(out); } else { encrypted = encryption.encrypt(out, this.password, encType); } out = JSON.stringify({ _encrypted: true, type: encType, data: encrypted }); } try { fs.writeFileSync(filePath, out, 'utf8'); } catch (err) { throw new Error(`Failed to export collection: ${err.message}`); } } /** * Imports data from a JSON file into a collection. * @param {string} collection - Collection name (key) * @param {string} filePath - Input file path */ importCollection(collection, filePath) { if (typeof collection !== 'string') throw new TypeError('Collection name must be a string'); if (!filePath || typeof filePath !== 'string') throw new TypeError('File path must be a string'); let fileContent; try { fileContent = fs.readFileSync(filePath, 'utf8'); } catch (err) { throw new Error(`Failed to read file: ${err.message}`); } let docs; try { const parsed = JSON.parse(fileContent); if (parsed && parsed._encrypted && parsed.data && this.password) { // Auto-decrypt if password is set let encType = parsed.type || this.encryptionType; let decrypted; if (encType === 'Base64') { decrypted = base64Decrypt(parsed.data); } else { decrypted = encryption.decrypt(parsed.data, this.password, encType); } docs = JSON.parse(decrypted); } else { docs = parsed; } } catch (err) { throw new Error(`Invalid JSON in import file: ${err.message}`); } if (!Array.isArray(docs) && typeof docs !== 'object') { throw new Error('Imported data must be an array or object.'); } // Create collection if it does not exist if (!this.data[collection]) this.data[collection] = Array.isArray(docs) ? [] : {}; // Insert logic: if array, push; if object, assign if (Array.isArray(this.data[collection]) && Array.isArray(docs)) { this.data[collection].push(...docs); } else if (typeof this.data[collection] === 'object' && typeof docs === 'object' && !Array.isArray(docs)) { Object.assign(this.data[collection], docs); } else { throw new Error('Collection type mismatch.'); } this._saveSync(); } constructor(options = {}) { this.filePath = options.filePath || path.resolve(process.cwd(), 'alphabase.json'); this.backupDir = options.backupDir || path.resolve(path.dirname(this.filePath), 'backups'); this.schema = options.schema || null; this.password = options.password || null; this.encryptionType = options.encryption || 'AES'; // Performance optimizations - instance-based for proper cleanup this.performanceMode = options.performanceMode !== false; // Default enabled this.optimizer = this.performanceMode ? new PerformanceOptimizer(this) : null; // Disable connection pool in test environment to prevent open handles const isTestEnv = process.env.NODE_ENV === 'test' || process.env.JEST_WORKER_ID || global.ALPHABASE_TEST_MODE || process.env.ALPHABASE_DISABLE_CONNECTION_POOL === 'true'; this.connectionPool = (options.useConnectionPool !== false && !isTestEnv) ? new ConnectionPool({ maxConnections: 10, minConnections: 2, idleTimeoutMs: 30000 // Shorter timeout for tests }) : null; this.fileLockManager = options.useFileLocking !== false ? new FileLockManager() : null; // Write optimization settings this.batchWriteEnabled = options.batchWrite !== false; this.deferredWriteTimeout = options.deferredWriteTimeout || 1000; this.writeQueue = []; this.writeTimer = null; this.ajv = this.schema ? new Ajv() : null; this.validator = this.schema ? this.ajv.compile(this.schema) : null; // Security features (optional) this.jwtAuth = options.jwtSecret ? new JWTAuth(options.jwtSecret) : null; this.auditLogger = options.audit ? new AuditLogger(options.audit) : null; this.dataIntegrity = options.integrity !== false ? new DataIntegrity() : null; this.rsaEncryption = options.rsa ? new RSAEncryption() : null; // Register for cleanup globalInstances.add(this); this._ensureFile(); this._loadSync(); this._backupInterval = null; if (options.autoBackupInterval) { this.startAutoBackup(options.autoBackupInterval); } // TTL metadata this.ttlMeta = {}; this._loadTTL(); } // Internal: Ensure DB file and backup dir exist _ensureFile() { if (!fs.existsSync(this.filePath)) { fs.writeFileSync(this.filePath, '{}', 'utf8'); } if (!fs.existsSync(this.backupDir)) { fs.mkdirSync(this.backupDir, { recursive: true }); } } // Internal: Load DB synchronously _loadSync() { try { const raw = fs.readFileSync(this.filePath, 'utf8'); let json; try { json = JSON.parse(raw); } catch (e) { // Dosya bozuksa veya şifreli ise json = null; } if (json && json._encrypted && json.data) { // Şifreli JSON sarmalayıcı try { const decrypted = encryption.decrypt(json.data, this.password, json.type || this.encryptionType); const parsed = JSON.parse(decrypted); this.data = parsed.data || {}; this.ttlMeta = parsed.ttlMeta || {}; } catch (err) { // Şifre çözme başarısız this.data = {}; this.ttlMeta = {}; } } else if (json) { this.data = json.data || json; this.ttlMeta = json.ttlMeta || {}; } else if (this.password || this.encryptionType !== 'None') { // Eski format: doğrudan şifreli string try { const decrypted = encryption.decrypt(raw, this.password, this.encryptionType); const parsed = JSON.parse(decrypted); this.data = parsed.data || {}; this.ttlMeta = parsed.ttlMeta || {}; } catch (err) { this.data = {}; this.ttlMeta = {}; } } else { this.data = {}; this.ttlMeta = {}; } this.cleanupSync(); } catch (e) { this.data = {}; this.ttlMeta = {}; } } // Internal: Load TTL metadata from file if exists _loadTTL() { if (this.ttlMeta && typeof this.ttlMeta === 'object') { this.cleanupSync(); } } // Remove expired keys (sync) cleanupSync() { const now = Date.now(); let changed = false; for (const key of Object.keys(this.ttlMeta)) { if (this.ttlMeta[key] && this.ttlMeta[key] < now) { delete this.data[key]; delete this.ttlMeta[key]; changed = true; } } if (changed) this._saveSync(); } // Enhanced cleanup with optimization async cleanup() { const now = Date.now(); let changed = false; const expiredKeys = []; // Collect expired keys first (avoid modifying during iteration) for (const key of Object.keys(this.ttlMeta)) { if (this.ttlMeta[key] && this.ttlMeta[key] < now) { expiredKeys.push(key); changed = true; } } // Remove expired keys and clear from cache for (const key of expiredKeys) { delete this.data[key]; delete this.ttlMeta[key]; if (this.optimizer) { this.optimizer.readCache.delete(key); } } if (changed) { // Use sync save for better performance this._saveSync(); // Audit expired keys cleanup if (this.auditLogger && expiredKeys.length > 0) { await this.auditLogger.log('ttl_cleanup', 'system', 'cleanup', { expiredKeys: expiredKeys.length, keys: expiredKeys.slice(0, 10) // Log first 10 keys }); } } } getTTL(key) { if (!this.ttlMeta[key]) return 0; const remaining = this.ttlMeta[key] - Date.now(); return remaining > 0 ? remaining : 0; } // Internal: Validate data if schema is set _validate(key, value) { if (this.validator) { const valid = this.validator(value); if (!valid) { throw new Error(`Schema validation failed for key '${key}': ${JSON.stringify(this.validator.errors)}`); } } } // Optimized synchronous methods with caching setSync(key, value, options = {}) { if (typeof key !== 'string') throw new TypeError('Key must be a string'); this._validate(key, value); // Performance optimization: check cache first if (this.optimizer) { const cached = this.optimizer.getCached(key); if (cached !== null && JSON.stringify(cached) === JSON.stringify(value)) { return; // No change, skip write } this.optimizer.setCached(key, value); } // Minimal change history & updatedAt if (typeof value === 'object' && value !== null) { this._addHistory(value, { type: 'set', value }); } this.data[key] = value; if (options.ttl) { this.ttlMeta[key] = Date.now() + options.ttl; } else { delete this.ttlMeta[key]; } this.cleanupSync(); // Deferred write for performance if (this.batchWriteEnabled && !options.immediate) { this._queueWrite(); } else { this._saveSync(); } } // Optimized read with intelligent caching getSync(key) { if (typeof key !== 'string') throw new TypeError('Key must be a string'); // Performance optimization: check cache first if (this.optimizer) { const cached = this.optimizer.getCached(key); if (cached !== null) { return cached; } } this.cleanupSync(); if (this.ttlMeta[key] && this.ttlMeta[key] < Date.now()) { delete this.data[key]; delete this.ttlMeta[key]; this._saveSync(); return undefined; } const value = this.data[key]; // Cache the result for future reads if (this.optimizer && value !== undefined) { this.optimizer.setCached(key, value); } return value; } deleteSync(key) { if (typeof key !== 'string') throw new TypeError('Key must be a string'); if (this.data[key] && typeof this.data[key] === 'object') { this._addHistory(this.data[key], { type: 'delete', value: this.data[key] }); } delete this.data[key]; delete this.ttlMeta[key]; this._saveSync(); } /** * Set TTL (ms) for a document in a collection (array or object) */ setTTL(collection, id, ttlMs) { const col = this.data[collection]; if (!col) throw new Error('Collection not found'); let doc; if (Array.isArray(col)) { doc = col.find(d => d.id === id); } else { doc = col[id]; } if (!doc) throw new Error('Document not found'); if (!this.ttlMeta[collection]) this.ttlMeta[collection] = {}; this.ttlMeta[collection][id] = Date.now() + ttlMs; this._saveSync(); } // Deferred write queue for batch processing _queueWrite() { if (this.writeTimer) return; // Already queued this.writeTimer = setTimeout(() => { this._saveSync(); this.writeTimer = null; }, this.deferredWriteTimeout); } // Force immediate write (flush queue) flushSync() { if (this.writeTimer) { clearTimeout(this.writeTimer); this.writeTimer = null; } this._saveSync(); } // Internal: Save DB synchronously (restored original method) _saveSync() { const getCircularReplacer = () => { const seen = new WeakSet(); return (key, value) => { if (typeof value === 'object' && value !== null) { if (seen.has(value)) return '[Circular]'; seen.add(value); } return value; }; }; const outObj = { data: this.data, ttlMeta: this.ttlMeta }; let out; let effectiveEncryptionType = this.encryptionType; if ((this.encryptionType === 'AES' || this.encryptionType === 'XOR') && !this.password) { effectiveEncryptionType = 'None'; } if (effectiveEncryptionType !== 'None') { let encrypted; if (effectiveEncryptionType === 'Base64') { encrypted = base64Encrypt(JSON.stringify(outObj, getCircularReplacer())); } else { encrypted = encryption.encrypt(JSON.stringify(outObj, getCircularReplacer()), this.password, effectiveEncryptionType); } out = JSON.stringify({ _encrypted: true, type: effectiveEncryptionType, data: encrypted }); } else { out = JSON.stringify(outObj, getCircularReplacer(), 2); } fs.writeFileSync(this.filePath, out, 'utf8'); } // Enhanced batch operations with connection pooling async batchAsync(ops) { if (!Array.isArray(ops)) throw new TypeError('Batch must be an array'); let connection; if (this.connectionPool) { connection = await this.connectionPool.acquire(); } try { // Acquire file lock for atomic batch operation if (this.fileLockManager) { await this.fileLockManager.acquireLock(this.filePath, true); } const startTime = Date.now(); // Process operations in memory first for (const op of ops) { if (op.op === 'set') { this.data[op.key] = op.value; if (op.options?.ttl) { this.ttlMeta[op.key] = Date.now() + op.options.ttl; } } else if (op.op === 'delete') { delete this.data[op.key]; delete this.ttlMeta[op.key]; } } // Single atomic write this._saveSync(); // Update performance metrics if (this.optimizer) { this.optimizer.metrics.batchedWrites += ops.length; this.optimizer.metrics.totalOperations += ops.length; } // Audit logging if (this.auditLogger && ops.length > 0) { await this.auditLogger.log('batch_operation', 'system', 'batch', { operations: ops.length, duration: Date.now() - startTime }); } } finally { // Release file lock if (this.fileLockManager) { this.fileLockManager.releaseLock(this.filePath, true); } // Release connection if (connection && this.connectionPool) { this.connectionPool.release(connection); } } } cleanupSync() { const now = Date.now(); let changed = false; // Support both old and new TTL structure for (const key of Object.keys(this.ttlMeta)) { if (typeof this.ttlMeta[key] === 'object') { // Per-document TTL for collections for (const docId of Object.keys(this.ttlMeta[key])) { if (this.ttlMeta[key][docId] < now) { if (this.data[key]) { if (Array.isArray(this.data[key])) { const idx = this.data[key].findIndex(d => d.id === docId); if (idx !== -1) this.data[key].splice(idx, 1); } else { delete this.data[key][docId]; } } delete this.ttlMeta[key][docId]; changed = true; } } } else if (this.ttlMeta[key] && this.ttlMeta[key] < now) { delete this.data[key]; delete this.ttlMeta[key]; changed = true; } } if (changed) this._saveSync(); } // Enhanced statistics with performance metrics statsSync() { const stats = fs.statSync(this.filePath); const keys = Object.keys(this.data); const totalKeys = keys.length; let totalValueSize = 0; let largestValueSize = 0; let largestKey = ''; for (const key of keys) { let valueSize = 0; try { valueSize = Buffer.byteLength(JSON.stringify(this.data[key]), 'utf8'); } catch { valueSize = 0; } totalValueSize += valueSize; if (valueSize > largestValueSize) largestValueSize = valueSize; if (key.length > largestKey.length) largestKey = key; } const averageValueSize = totalKeys > 0 ? Math.round(totalValueSize / totalKeys) : 0; const baseStats = { totalKeys, fileSize: stats.size, lastModified: new Date(stats.mtime), memoryUsage: process.memoryUsage().heapUsed, averageValueSize, largestKey, largestValueSize }; // Add performance metrics if optimizer is enabled if (this.optimizer) { return { ...baseStats, performance: this.optimizer.getPerformanceMetrics(), memory: this.optimizer.getMemoryStats(), connectionPool: this.connectionPool ? this.connectionPool.getStats() : null }; } return baseStats; } importSync(data, ttlMeta = {}) { let importData = data; if (typeof data === 'string') { try { importData = JSON.parse(data); } catch (e) { // Şifreli JSON olabilir try { const parsed = JSON.parse(data); if (parsed._encrypted && parsed.data) { let encType = parsed.type || this.encryptionType; let decrypted; if (encType === 'Base64') { decrypted = base64Decrypt(parsed.data); } else { decrypted = encryption.decrypt(parsed.data, this.password, encType); } importData = JSON.parse(decrypted); } else { throw new Error('Invalid import string'); } } catch (err) { throw new Error('Import failed: ' + err.message); } } } if (typeof importData !== 'object' || Array.isArray(importData)) { throw new TypeError('Import data must be an object or JSON string'); } this.data = { ...(importData.data || importData) }; this.ttlMeta = { ...(importData.ttlMeta || ttlMeta) }; const outObj = { data: this.data, ttlMeta: this.ttlMeta }; let out; let effectiveEncryptionType = this.encryptionType; if ((this.encryptionType === 'AES' || this.encryptionType === 'XOR') && !this.password) { effectiveEncryptionType = 'None'; } if (effectiveEncryptionType !== 'None') { const encrypted = encryption.encrypt(JSON.stringify(outObj), this.password, effectiveEncryptionType); out = JSON.stringify({ _encrypted: true, type: effectiveEncryptionType, data: encrypted }); } else { out = JSON.stringify(outObj, null, 2); } fs.writeFileSync(this.filePath, out, 'utf8'); } exportSync(asString = false) { const outObj = { data: this.data, ttlMeta: this.ttlMeta }; if (asString) return JSON.stringify(outObj, null, 2); return { ...this.data }; } backupSync() { const ts = new Date().toISOString().replace(/[:.]/g, '-'); const backupPath = path.join(this.backupDir, `backup-${ts}.json`); const outObj = { data: this.data, ttlMeta: this.ttlMeta }; let out; let effectiveEncryptionType = this.encryptionType; if ((this.encryptionType === 'AES' || this.encryptionType === 'XOR') && !this.password) { effectiveEncryptionType = 'None'; } if (effectiveEncryptionType !== 'None') { let encrypted; if (effectiveEncryptionType === 'Base64') { encrypted = base64Encrypt(JSON.stringify(outObj)); } else { encrypted = encryption.encrypt(JSON.stringify(outObj), this.password, effectiveEncryptionType); } out = JSON.stringify({ _encrypted: true, type: effectiveEncryptionType, data: encrypted }); } else { out = JSON.stringify(outObj, null, 2); } fs.writeFileSync(backupPath, out, 'utf8'); return backupPath; } // Asynchronous methods async set(key, value, options = {}) { if (typeof key !== 'string') throw new TypeError('Key must be a string'); this._validate(key, value); this.data[key] = value; if (options.ttl) { this.ttlMeta[key] = Date.now() + options.ttl; } else { delete this.ttlMeta[key]; } await this.cleanup(); let outObj = { data: this.data, ttlMeta: this.ttlMeta }; let effectiveEncryptionType = this.encryptionType; if ((this.encryptionType === 'AES' || this.encryptionType === 'XOR') && !this.password) { effectiveEncryptionType = 'None'; } let out; if (effectiveEncryptionType !== 'None') { const encrypted = encryption.encrypt(JSON.stringify(outObj), this.password, effectiveEncryptionType); out = JSON.stringify({ _encrypted: true, type: effectiveEncryptionType, data: encrypted }); } else { out = JSON.stringify(outObj, null, 2); } await fs.promises.writeFile(this.filePath, out, 'utf8'); } async get(key) { if (typeof key !== 'string') throw new TypeError('Key must be a string'); await this.cleanup(); if (this.ttlMeta[key] && this.ttlMeta[key] < Date.now()) { delete this.data[key]; delete this.ttlMeta[key]; await this._saveSync(); return undefined; } return this.data[key]; } async delete(key) { if (typeof key !== 'string') throw new TypeError('Key must be a string'); delete this.data[key]; delete this.ttlMeta[key]; let outObj = { data: this.data, ttlMeta: this.ttlMeta }; let effectiveEncryptionType = this.encryptionType; if ((this.encryptionType === 'AES' || this.encryptionType === 'XOR') && !this.password) { effectiveEncryptionType = 'None'; } let out; if (effectiveEncryptionType !== 'None') { const encrypted = encryption.encrypt(JSON.stringify(outObj), this.password, effectiveEncryptionType); out = JSON.stringify({ _encrypted: true, type: effectiveEncryptionType, data: encrypted }); } else { out = JSON.stringify(outObj, null, 2); } await fs.promises.writeFile(this.filePath, out, 'utf8'); } async has(key) { if (typeof key !== 'string') throw new TypeError('Key must be a string'); await this.cleanup(); if (this.ttlMeta[key] && this.ttlMeta[key] < Date.now()) { delete this.data[key]; delete this.ttlMeta[key]; await this._saveSync(); return false; } return Object.prototype.hasOwnProperty.call(this.data, key); } async clear() { this.data = {}; this.ttlMeta = {}; let outObj = { data: this.data, ttlMeta: this.ttlMeta }; let effectiveEncryptionType = this.encryptionType; if ((this.encryptionType === 'AES' || this.encryptionType === 'XOR') && !this.password) { effectiveEncryptionType = 'None'; } let out; if (effectiveEncryptionType !== 'None') { const encrypted = encryption.encrypt(JSON.stringify(outObj), this.password, effectiveEncryptionType); out = JSON.stringify({ _encrypted: true, type: effectiveEncryptionType, data: encrypted }); } else { out = JSON.stringify(outObj, null, 2); } await fs.promises.writeFile(this.filePath, out, 'utf8'); } async all() { await this.cleanup(); const out = {}; for (const key of Object.keys(this.data)) { if (!this.ttlMeta[key] || this.ttlMeta[key] > Date.now()) { out[key] = this.data[key]; } } return out; } async stats() { const stats = await fs.promises.stat(this.filePath); const keys = Object.keys(this.data); const totalKeys = keys.length; let totalValueSize = 0; let largestValueSize = 0; let largestKey = ''; for (const key of keys) { let valueSize = 0; try { valueSize = Buffer.byteLength(JSON.stringify(this.data[key]), 'utf8'); } catch { valueSize = 0; } totalValueSize += valueSize; if (valueSize > largestValueSize) largestValueSize = valueSize; if (key.length > largestKey.length) largestKey = key; } const averageValueSize = totalKeys > 0 ? Math.round(totalValueSize / totalKeys) : 0; return { totalKeys, fileSize: stats.size, lastModified: new Date(stats.mtime), memoryUsage: process.memoryUsage().heapUsed, averageValueSize, largestKey, largestValueSize }; } async import(data, ttlMeta = {}) { let importData = data; if (typeof data === 'string') { try { importData = JSON.parse(data); } catch (e) { // Şifreli JSON olabilir try { const parsed = JSON.parse(data); if (parsed._encrypted && parsed.data) { const decrypted = encryption.decrypt(parsed.data, this.password, parsed.type || this.encryptionType); importData = JSON.parse(decrypted); } else { throw new Error('Invalid import string'); } } catch (err) { throw new Error('Import failed: ' + err.message); } } } if (typeof importData !== 'object' || Array.isArray(importData)) { throw new TypeError('Import data must be an object or JSON string'); } this.data = { ...(importData.data || importData) }; this.ttlMeta = { ...(importData.ttlMeta || ttlMeta) }; let outObj = { data: this.data, ttlMeta: this.ttlMeta }; let effectiveEncryptionType = this.encryptionType; if ((this.encryptionType === 'AES' || this.encryptionType === 'XOR') && !this.password) { effectiveEncryptionType = 'None'; } let out; if (effectiveEncryptionType !== 'None') { const encrypted = encryption.encrypt(JSON.stringify(outObj), this.password, effectiveEncryptionType); out = JSON.stringify({ _encrypted: true, type: effectiveEncryptionType, data: encrypted }); } else { out = JSON.stringify(outObj, null, 2); } await fs.promises.writeFile(this.filePath, out, 'utf8'); } async export(asString = false) { const outObj = { data: this.data, ttlMeta: this.ttlMeta }; if (asString) return JSON.stringify(outObj, null, 2); return { ...this.data }; } async backup() { const ts = new Date().toISOString().replace(/[:.]/g, '-'); const backupPath = path.join(this.backupDir, `backup-${ts}.json`); let outObj = { data: this.data, ttlMeta: this.ttlMeta }; let effectiveEncryptionType = this.encryptionType; if ((this.encryptionType === 'AES' || this.encryptionType === 'XOR') && !this.password) { effectiveEncryptionType = 'None'; } let out; if (effectiveEncryptionType !== 'None') { const encrypted = encryption.encrypt(JSON.stringify(outObj), this.password, effectiveEncryptionType); out = JSON.stringify({ _encrypted: true, type: effectiveEncryptionType, data: encrypted }); } else { out = JSON.stringify(outObj, null, 2); } await fs.promises.writeFile(backupPath, out, 'utf8'); return backupPath; } startAutoBackup(intervalMs) { if (this._backupInterval) clearInterval(this._backupInterval); this._backupInterval = setInterval(() => { try { this.backupSync(); } catch (e) { // Ignore backup errors } }, intervalMs); } stopAutoBackup() { if (this._backupInterval) clearInterval(this._backupInterval); this._backupInterval = null; } // Security Methods (V3.0.0) /** * Create JWT token for authentication * @param {Object} payload - Token payload * @param {Object} options - Token options * @returns {string} JWT token */ createToken(payload, options = {}) { if (!this.jwtAuth) throw new Error('JWT authentication not enabled'); return this.jwtAuth.createToken(payload, options); } /** * Verify JWT token * @param {string} token - JWT token to verify * @returns {Object} Verification result */ verifyToken(token) { if (!this.jwtAuth) throw new Error('JWT authentication not enabled'); return this.jwtAuth.verifyToken(token); } /** * Log operation for audit trail * @param {string} operation - Operation type * @param {string} key - Key involved * @param {string} user - User performing operation * @param {Object} metadata - Additional metadata */ auditLog(operation, key, user, metadata = {}) { if (this.auditLogger) { this.auditLogger.log(operation, key, user, metadata); } } /** * Get audit logs * @param {Object} options - Filter options * @returns {Array} Audit log entries */ getAuditLogs(options = {}) { if (!this.auditLogger) throw new Error('Audit logging not enabled'); return this.auditLogger.getLogs(options); } /** * Verify data integrity * @param {string} key - Key to verify * @returns {boolean} Integrity check result */ verifyIntegrity(key) { if (!this.dataIntegrity) return true; const value = this.data[key]; if (!value) return false; return this.dataIntegrity.verify(JSON.stringify(value), this._getStoredHash(key)); } /** * Generate RSA key pair * @returns {Object} Key pair object */ generateRSAKeys() { if (!this.rsaEncryption) throw new Error('RSA encryption not enabled'); return this.rsaEncryption.generateKeyPair(); } /** * Encrypt data with RSA * @param {string} data - Data to encrypt * @param {string} publicKey - RSA public key * @returns {string} Encrypted data */ rsaEncrypt(data, publicKey) { if (!this.rsaEncryption) throw new Error('RSA encryption not enabled'); return this.rsaEncryption.encrypt(data, publicKey); } /** * Decrypt data with RSA * @param {string} encryptedData - Encrypted data * @param {string} privateKey - RSA private key * @returns {string} Decrypted data */ rsaDecrypt(encryptedData, privateKey) { if (!this.rsaEncryption) throw new Error('RSA encryption not enabled'); return this.rsaEncryption.decrypt(encryptedData, privateKey); } // Private helper methods for integrity _getStoredHash(key) { // In a real implementation, this would retrieve stored hash from metadata // For now, return null to indicate no stored hash return null; } _storeHash(key, hash) { // In a real implementation, this would store hash in metadata // For now, this is a placeholder } /** * Professional shutdown method for resource cleanup * Ensures all resources are properly released */ async shutdown() { try { // Flush any pending writes if (this.writeTimer) { clearTimeout(this.writeTimer); this.writeTimer = null; this._saveSync(); } // Stop all intervals this.stopScheduledCleanup(); this.stopAutoBackup(); // Shutdown connection pool if (this.connectionPool) { await this.connectionPool.shutdown(); } // Shutdown performance optimizer if (this.optimizer) { await this.optimizer.shutdown(); } // Clear all caches if (this.optimizer) { this.optimizer.clearCaches(); } // Remove from global instances globalInstances.delete(this); // Audit final shutdown if (this.auditLogger) { await this.auditLogger.log('database_shutdown', 'system', 'shutdown', { timestamp: new Date().toISOString(), totalKeys: Object.keys(this.data).length }); } return true; } catch (error) { console.error('Error during shutdown:', error); return false; } } // Alias for shutdown (for cleanup compatibility) async cleanup() { return this.shutdown(); } } module.exports = AlphaBase;