180 lines
4.8 KiB
TypeScript
180 lines
4.8 KiB
TypeScript
import * as plugins from '../plugins.js';
|
|
import { logger } from '../logger.js';
|
|
import { defaultTsmDbPath } from '../paths.js';
|
|
|
|
/**
|
|
* Configuration options for the unified DCRouter database
|
|
*/
|
|
export interface IDcRouterDbConfig {
|
|
/** External MongoDB connection URL. If absent, uses embedded LocalSmartDb. */
|
|
mongoDbUrl?: string;
|
|
/** Storage path for embedded LocalSmartDb data (default: ~/.serve.zone/dcrouter/tsmdb) */
|
|
storagePath?: string;
|
|
/** Database name (default: dcrouter) */
|
|
dbName?: string;
|
|
/** Enable debug logging */
|
|
debug?: boolean;
|
|
}
|
|
|
|
/**
|
|
* DcRouterDb - Unified database layer for DCRouter
|
|
*
|
|
* Replaces both StorageManager (flat-file key-value) and CacheDb (embedded MongoDB).
|
|
* All data is stored as smartdata document classes in a single database.
|
|
*
|
|
* Two modes:
|
|
* - **Embedded** (default): Spawns a LocalSmartDb (Rust-based MongoDB-compatible engine)
|
|
* - **External**: Connects to a provided MongoDB URL
|
|
*/
|
|
export class DcRouterDb {
|
|
private static instance: DcRouterDb | null = null;
|
|
|
|
private localSmartDb: plugins.smartdb.LocalSmartDb | null = null;
|
|
private smartdataDb!: plugins.smartdata.SmartdataDb;
|
|
private options: Required<IDcRouterDbConfig>;
|
|
private isStarted: boolean = false;
|
|
|
|
constructor(options: IDcRouterDbConfig = {}) {
|
|
this.options = {
|
|
mongoDbUrl: options.mongoDbUrl || '',
|
|
storagePath: options.storagePath || defaultTsmDbPath,
|
|
dbName: options.dbName || 'dcrouter',
|
|
debug: options.debug || false,
|
|
};
|
|
}
|
|
|
|
/**
|
|
* Get or create the singleton instance
|
|
*/
|
|
public static getInstance(options?: IDcRouterDbConfig): DcRouterDb {
|
|
if (!DcRouterDb.instance) {
|
|
DcRouterDb.instance = new DcRouterDb(options);
|
|
}
|
|
return DcRouterDb.instance;
|
|
}
|
|
|
|
/**
|
|
* Reset the singleton instance (useful for testing)
|
|
*/
|
|
public static resetInstance(): void {
|
|
DcRouterDb.instance = null;
|
|
}
|
|
|
|
/**
|
|
* Start the database
|
|
* - If mongoDbUrl is provided, connects directly to external MongoDB
|
|
* - Otherwise, starts an embedded LocalSmartDb instance
|
|
*/
|
|
public async start(): Promise<void> {
|
|
if (this.isStarted) {
|
|
logger.log('warn', 'DcRouterDb already started');
|
|
return;
|
|
}
|
|
|
|
try {
|
|
let connectionUri: string;
|
|
|
|
if (this.options.mongoDbUrl) {
|
|
// External MongoDB mode
|
|
connectionUri = this.options.mongoDbUrl;
|
|
logger.log('info', `DcRouterDb connecting to external MongoDB`);
|
|
} else {
|
|
// Embedded LocalSmartDb mode
|
|
await plugins.fsUtils.ensureDir(this.options.storagePath);
|
|
|
|
this.localSmartDb = new plugins.smartdb.LocalSmartDb({
|
|
folderPath: this.options.storagePath,
|
|
});
|
|
|
|
const connectionInfo = await this.localSmartDb.start();
|
|
connectionUri = connectionInfo.connectionUri;
|
|
|
|
if (this.options.debug) {
|
|
logger.log('debug', `LocalSmartDb started with URI: ${connectionUri}`);
|
|
}
|
|
|
|
logger.log('info', `DcRouterDb started embedded instance at ${this.options.storagePath}`);
|
|
}
|
|
|
|
// Initialize smartdata ORM
|
|
this.smartdataDb = new plugins.smartdata.SmartdataDb({
|
|
mongoDbUrl: connectionUri,
|
|
mongoDbName: this.options.dbName,
|
|
});
|
|
await this.smartdataDb.init();
|
|
|
|
this.isStarted = true;
|
|
logger.log('info', `DcRouterDb ready (db: ${this.options.dbName})`);
|
|
} catch (error: unknown) {
|
|
logger.log('error', `Failed to start DcRouterDb: ${(error as Error).message}`);
|
|
throw error;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Stop the database
|
|
*/
|
|
public async stop(): Promise<void> {
|
|
if (!this.isStarted) {
|
|
return;
|
|
}
|
|
|
|
try {
|
|
// Close smartdata connection
|
|
if (this.smartdataDb) {
|
|
await this.smartdataDb.close();
|
|
}
|
|
|
|
// Stop embedded LocalSmartDb if running
|
|
if (this.localSmartDb) {
|
|
await this.localSmartDb.stop();
|
|
this.localSmartDb = null;
|
|
}
|
|
|
|
this.isStarted = false;
|
|
logger.log('info', 'DcRouterDb stopped');
|
|
} catch (error: unknown) {
|
|
logger.log('error', `Error stopping DcRouterDb: ${(error as Error).message}`);
|
|
throw error;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Get the smartdata database instance for @Collection decorators
|
|
*/
|
|
public getDb(): plugins.smartdata.SmartdataDb {
|
|
if (!this.isStarted) {
|
|
throw new Error('DcRouterDb not started. Call start() first.');
|
|
}
|
|
return this.smartdataDb;
|
|
}
|
|
|
|
/**
|
|
* Check if the database is ready
|
|
*/
|
|
public isReady(): boolean {
|
|
return this.isStarted;
|
|
}
|
|
|
|
/**
|
|
* Whether running in embedded mode (LocalSmartDb) vs external MongoDB
|
|
*/
|
|
public isEmbedded(): boolean {
|
|
return !this.options.mongoDbUrl;
|
|
}
|
|
|
|
/**
|
|
* Get the storage path (only relevant for embedded mode)
|
|
*/
|
|
public getStoragePath(): string {
|
|
return this.options.storagePath;
|
|
}
|
|
|
|
/**
|
|
* Get the database name
|
|
*/
|
|
public getDbName(): string {
|
|
return this.options.dbName;
|
|
}
|
|
}
|