import * as plugins from '../plugins.js'; import { logger } from '../logger.js'; /** * Configuration options for CacheDb */ export interface ICacheDbOptions { /** Base storage path for TsmDB data (default: /etc/dcrouter/tsmdb) */ storagePath?: string; /** Database name (default: dcrouter) */ dbName?: string; /** Enable debug logging */ debug?: boolean; } /** * CacheDb - Wrapper around LocalTsmDb and smartdata * * Provides persistent caching using smartdata as the ORM layer * and LocalTsmDb as the embedded database engine. */ export class CacheDb { private static instance: CacheDb | null = null; private localTsmDb: plugins.smartmongo.LocalTsmDb; private smartdataDb: plugins.smartdata.SmartdataDb; private options: Required; private isStarted: boolean = false; constructor(options: ICacheDbOptions = {}) { this.options = { storagePath: options.storagePath || '/etc/dcrouter/tsmdb', dbName: options.dbName || 'dcrouter', debug: options.debug || false, }; } /** * Get or create the singleton instance */ public static getInstance(options?: ICacheDbOptions): CacheDb { if (!CacheDb.instance) { CacheDb.instance = new CacheDb(options); } return CacheDb.instance; } /** * Reset the singleton instance (useful for testing) */ public static resetInstance(): void { CacheDb.instance = null; } /** * Start the cache database * - Initializes LocalTsmDb with file persistence * - Connects smartdata to the LocalTsmDb via Unix socket */ public async start(): Promise { if (this.isStarted) { logger.log('warn', 'CacheDb already started'); return; } try { // Ensure storage directory exists await plugins.fsUtils.ensureDir(this.options.storagePath); // Create LocalTsmDb instance this.localTsmDb = new plugins.smartmongo.LocalTsmDb({ dbDir: this.options.storagePath, }); // Start LocalTsmDb and get connection URI await this.localTsmDb.start(); const mongoDescriptor = this.localTsmDb.mongoDescriptor; if (this.options.debug) { logger.log('debug', `LocalTsmDb started with descriptor: ${JSON.stringify(mongoDescriptor)}`); } // Initialize smartdata with the connection this.smartdataDb = new plugins.smartdata.SmartdataDb(mongoDescriptor); await this.smartdataDb.init(); this.isStarted = true; logger.log('info', `CacheDb started at ${this.options.storagePath}`); } catch (error) { logger.log('error', `Failed to start CacheDb: ${error.message}`); throw error; } } /** * Stop the cache database */ public async stop(): Promise { if (!this.isStarted) { return; } try { // Close smartdata connection if (this.smartdataDb) { await this.smartdataDb.close(); } // Stop LocalTsmDb if (this.localTsmDb) { await this.localTsmDb.stop(); } this.isStarted = false; logger.log('info', 'CacheDb stopped'); } catch (error) { logger.log('error', `Error stopping CacheDb: ${error.message}`); throw error; } } /** * Get the smartdata database instance */ public getDb(): plugins.smartdata.SmartdataDb { if (!this.isStarted) { throw new Error('CacheDb not started. Call start() first.'); } return this.smartdataDb; } /** * Check if the database is ready */ public isReady(): boolean { return this.isStarted; } /** * Get the storage path */ public getStoragePath(): string { return this.options.storagePath; } /** * Get the database name */ public getDbName(): string { return this.options.dbName; } }