UNPKG

@ahmedhegazee/nestjs-telescope

Version:

Advanced observability and monitoring solution for NestJS applications with ML-powered analytics, enterprise features, and production-ready scaling

302 lines 11.9 kB
"use strict"; var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { if (k2 === undefined) k2 = k; var desc = Object.getOwnPropertyDescriptor(m, k); if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { desc = { enumerable: true, get: function() { return m[k]; } }; } Object.defineProperty(o, k2, desc); }) : (function(o, m, k, k2) { if (k2 === undefined) k2 = k; o[k2] = m[k]; })); var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { Object.defineProperty(o, "default", { enumerable: true, value: v }); }) : function(o, v) { o["default"] = v; }); var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; return c > 3 && r && Object.defineProperty(target, key, r), r; }; var __importStar = (this && this.__importStar) || function (mod) { if (mod && mod.__esModule) return mod; var result = {}; if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); __setModuleDefault(result, mod); return result; }; var __metadata = (this && this.__metadata) || function (k, v) { if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v); }; var FileStorageDriver_1; Object.defineProperty(exports, "__esModule", { value: true }); exports.FileStorageDriver = void 0; const common_1 = require("@nestjs/common"); const fs_1 = require("fs"); const path = __importStar(require("path")); let FileStorageDriver = FileStorageDriver_1 = class FileStorageDriver { constructor(config) { this.logger = new common_1.Logger(FileStorageDriver_1.name); this.index = new Map(); this.initialized = false; this.storageDir = config?.file?.directory || './telescope-storage'; this.indexFile = path.join(this.storageDir, 'index.json'); this.initializeStorage(); } async initializeStorage() { if (this.initialized) return; try { await fs_1.promises.mkdir(this.storageDir, { recursive: true }); await this.loadIndex(); this.initialized = true; this.logger.log(`File storage initialized at: ${this.storageDir}`); } catch (error) { this.logger.error('Failed to initialize file storage:', error); throw error; } } async store(entry) { await this.ensureInitialized(); const filename = `${entry.id}.json`; const filepath = path.join(this.storageDir, filename); try { await fs_1.promises.writeFile(filepath, JSON.stringify(entry, null, 2)); this.index.set(entry.id, { filename, type: entry.type, timestamp: entry.timestamp, tags: entry.tags, familyHash: entry.familyHash, sequence: entry.sequence }); await this.saveIndex(); this.logger.debug(`Stored entry: ${entry.id}`); } catch (error) { this.logger.error(`Failed to store entry ${entry.id}:`, error); throw error; } } async storeBatch(entries) { await this.ensureInitialized(); try { const concurrency = 5; const chunks = this.chunkArray(entries, concurrency); for (const chunk of chunks) { await Promise.all(chunk.map(entry => this.store(entry))); } this.logger.debug(`Stored batch of ${entries.length} entries`); } catch (error) { this.logger.error('Failed to store batch:', error); throw error; } } async find(filter) { await this.ensureInitialized(); let filteredEntries = Array.from(this.index.values()); if (filter?.type) { filteredEntries = filteredEntries.filter(entry => entry.type === filter.type); } if (filter?.tags && filter.tags.length > 0) { filteredEntries = filteredEntries.filter(entry => filter.tags.some(tag => entry.tags.includes(tag))); } if (filter?.dateFrom) { filteredEntries = filteredEntries.filter(entry => entry.timestamp >= filter.dateFrom); } if (filter?.dateTo) { filteredEntries = filteredEntries.filter(entry => entry.timestamp <= filter.dateTo); } filteredEntries.sort((a, b) => b.timestamp.getTime() - a.timestamp.getTime()); const offset = filter?.offset || 0; const limit = filter?.limit || 100; const total = filteredEntries.length; const paginatedEntries = filteredEntries.slice(offset, offset + limit); const entries = await Promise.all(paginatedEntries.map(async (indexEntry) => { try { const filepath = path.join(this.storageDir, indexEntry.filename); const content = await fs_1.promises.readFile(filepath, 'utf-8'); return JSON.parse(content); } catch (error) { this.logger.error(`Failed to load entry ${indexEntry.filename}:`, error); this.index.delete(indexEntry.filename.replace('.json', '')); return null; } })); return { entries: entries.filter(entry => entry !== null), total, hasMore: offset + limit < total }; } async findById(id) { await this.ensureInitialized(); const indexEntry = this.index.get(id); if (!indexEntry) return null; try { const filepath = path.join(this.storageDir, indexEntry.filename); const content = await fs_1.promises.readFile(filepath, 'utf-8'); return JSON.parse(content); } catch (error) { this.logger.error(`Failed to load entry ${id}:`, error); this.index.delete(id); await this.saveIndex(); return null; } } async delete(id) { await this.ensureInitialized(); const indexEntry = this.index.get(id); if (!indexEntry) return false; try { const filepath = path.join(this.storageDir, indexEntry.filename); await fs_1.promises.unlink(filepath); this.index.delete(id); await this.saveIndex(); return true; } catch (error) { this.logger.error(`Failed to delete entry ${id}:`, error); return false; } } async clear() { await this.ensureInitialized(); try { const files = await fs_1.promises.readdir(this.storageDir); const deletePromises = files .filter(file => file.endsWith('.json') && file !== 'index.json') .map(file => fs_1.promises.unlink(path.join(this.storageDir, file))); await Promise.all(deletePromises); this.index.clear(); await this.saveIndex(); this.logger.log('File storage cleared'); } catch (error) { this.logger.error('Failed to clear file storage:', error); throw error; } } async prune(olderThan) { await this.ensureInitialized(); const entriesToDelete = Array.from(this.index.entries()) .filter(([_, entry]) => entry.timestamp < olderThan); let deletedCount = 0; for (const [id, entry] of entriesToDelete) { try { const filepath = path.join(this.storageDir, entry.filename); await fs_1.promises.unlink(filepath); this.index.delete(id); deletedCount++; } catch (error) { this.logger.error(`Failed to delete entry ${id} during pruning:`, error); } } if (deletedCount > 0) { await this.saveIndex(); this.logger.log(`Pruned ${deletedCount} entries`); } return deletedCount; } async getStats() { await this.ensureInitialized(); const entries = Array.from(this.index.values()); const entriesByType = {}; let oldestEntry; let newestEntry; for (const entry of entries) { entriesByType[entry.type] = (entriesByType[entry.type] || 0) + 1; if (!oldestEntry || entry.timestamp < oldestEntry) { oldestEntry = entry.timestamp; } if (!newestEntry || entry.timestamp > newestEntry) { newestEntry = entry.timestamp; } } let sizeInBytes = 0; try { const files = await fs_1.promises.readdir(this.storageDir); for (const file of files) { const filepath = path.join(this.storageDir, file); const stats = await fs_1.promises.stat(filepath); sizeInBytes += stats.size; } } catch (error) { this.logger.error('Failed to calculate storage size:', error); } return { totalEntries: entries.length, entriesByType, oldestEntry, newestEntry, sizeInBytes }; } async healthCheck() { try { await this.ensureInitialized(); const testFile = path.join(this.storageDir, 'health-check.json'); await fs_1.promises.writeFile(testFile, JSON.stringify({ test: true })); await fs_1.promises.readFile(testFile); await fs_1.promises.unlink(testFile); return true; } catch (error) { return false; } } async loadIndex() { try { const content = await fs_1.promises.readFile(this.indexFile, 'utf-8'); const indexData = JSON.parse(content); for (const [id, entry] of Object.entries(indexData)) { entry.timestamp = new Date(entry.timestamp); } this.index = new Map(Object.entries(indexData)); this.logger.debug(`Loaded index with ${this.index.size} entries`); } catch (error) { this.index = new Map(); this.logger.debug('Started with empty index'); } } async saveIndex() { try { const indexData = Object.fromEntries(this.index); await fs_1.promises.writeFile(this.indexFile, JSON.stringify(indexData, null, 2)); } catch (error) { this.logger.error('Failed to save index:', error); throw error; } } async ensureInitialized() { if (!this.initialized) { await this.initializeStorage(); } } chunkArray(array, chunkSize) { const chunks = []; for (let i = 0; i < array.length; i += chunkSize) { chunks.push(array.slice(i, i + chunkSize)); } return chunks; } }; exports.FileStorageDriver = FileStorageDriver; exports.FileStorageDriver = FileStorageDriver = FileStorageDriver_1 = __decorate([ (0, common_1.Injectable)(), __metadata("design:paramtypes", [Object]) ], FileStorageDriver); //# sourceMappingURL=file-storage.driver.js.map