From ab99366a0a9479f104e831c6e1fe85d5579fb4ef Mon Sep 17 00:00:00 2001 From: fullex <0xfullex@gmail.com> Date: Fri, 21 Nov 2025 13:29:24 +0800 Subject: [PATCH] feat: enhance DbService with improved error handling and documentation - Added detailed JSDoc comments for better understanding of DbService methods and usage. - Implemented error handling during database initialization and migration processes to ensure robustness. - Introduced a method to check if the database is initialized before accessing it. - Updated the migrateSeed method to throw errors on failure, improving error reporting. --- src/main/data/db/DbService.ts | 94 ++++++++++++++++++++++++++++++----- 1 file changed, 82 insertions(+), 12 deletions(-) diff --git a/src/main/data/db/DbService.ts b/src/main/data/db/DbService.ts index 6f136a58a6..db5f288b6b 100644 --- a/src/main/data/db/DbService.ts +++ b/src/main/data/db/DbService.ts @@ -13,17 +13,52 @@ const logger = loggerService.withContext('DbService') const DB_NAME = 'cherrystudio.sqlite' const MIGRATIONS_BASE_PATH = 'migrations/sqlite-drizzle' +/** + * Database service managing SQLite connection via Drizzle ORM + * Implements singleton pattern for centralized database access + * + * Features: + * - Database initialization and connection management + * - Migration and seeding support + * + * @example + * ```typescript + * import { dbService } from '@data/db/DbService' + * + * // Run migrations + * await dbService.migrateDb() + * + * // Get database instance + * const db = dbService.getDb() + * ``` + */ class DbService { private static instance: DbService private db: DbType + private isInitialized = false private constructor() { - this.db = drizzle({ - connection: { url: pathToFileURL(path.join(app.getPath('userData'), DB_NAME)).href }, - casing: 'snake_case' - }) + try { + this.db = drizzle({ + connection: { url: pathToFileURL(path.join(app.getPath('userData'), DB_NAME)).href }, + casing: 'snake_case' + }) + this.isInitialized = true + logger.info('Database connection initialized', { + dbPath: path.join(app.getPath('userData'), DB_NAME) + }) + } catch (error) { + logger.error('Failed to initialize database connection', error as Error) + throw new Error('Database initialization failed') + } } + /** + * Get singleton instance of DbService + * Creates a new instance if one doesn't exist + * @returns {DbService} The singleton DbService instance + * @throws {Error} If database initialization fails + */ public static getInstance(): DbService { if (!DbService.instance) { DbService.instance = new DbService() @@ -31,23 +66,58 @@ class DbService { return DbService.instance } - public async migrateDb() { - const migrationsFolder = this.getMigrationsFolder() - await migrate(this.db, { migrationsFolder }) + /** + * Run database migrations + * @throws {Error} If migration fails + */ + public async migrateDb(): Promise { + try { + const migrationsFolder = this.getMigrationsFolder() + await migrate(this.db, { migrationsFolder }) + + logger.info('Database migration completed successfully') + } catch (error) { + logger.error('Database migration failed', error as Error) + throw error + } } + /** + * Get the database instance + * @throws {Error} If database is not initialized + */ public getDb(): DbType { + if (!this.isInitialized) { + throw new Error('Database is not initialized') + } return this.db } - public async migrateSeed(seedName: keyof typeof Seeding): Promise { + /** + * Check if database is initialized + */ + public isReady(): boolean { + return this.isInitialized + } + + /** + * Run seed data migration + * @param seedName - Name of the seed to run + * @throws {Error} If seed migration fails + */ + public async migrateSeed(seedName: keyof typeof Seeding): Promise { try { const Seed = Seeding[seedName] + if (!Seed) { + throw new Error(`Seed "${seedName}" not found`) + } + await new Seed().migrate(this.db) - return true + + logger.info('Seed migration completed successfully', { seedName }) } catch (error) { - logger.error('migration seeding failed', error as Error) - return false + logger.error('Seed migration failed', error as Error, { seedName }) + throw error } } @@ -55,7 +125,7 @@ class DbService { * Get the migrations folder based on the app's packaging status * @returns The path to the migrations folder */ - private getMigrationsFolder() { + private getMigrationsFolder(): string { if (app.isPackaged) { //see electron-builder.yml, extraResources from/to return path.join(process.resourcesPath, MIGRATIONS_BASE_PATH)