116 lines
4.1 KiB
TypeScript
116 lines
4.1 KiB
TypeScript
import { Database, open } from 'sqlite';
|
|
import sqlite3 from 'sqlite3';
|
|
import * as fs from 'fs/promises';
|
|
import * as path from 'path';
|
|
|
|
/**
|
|
* Singleton class responsible for initializing and setting up the SQLite database.
|
|
* It ensures that only one instance of the database is created and utilized throughout the application.
|
|
*/
|
|
export class DatabaseInitializer {
|
|
private static instance: DatabaseInitializer;
|
|
private db: Database | undefined;
|
|
private initializationPromise: Promise<void> | undefined;
|
|
|
|
/**
|
|
* The DatabaseInitializer's constructor is private to prevent direct instantiation with the `new` operator
|
|
* and ensure the Singleton pattern is followed.
|
|
*/
|
|
private constructor() { }
|
|
|
|
/**
|
|
* Gets the singleton instance of the DatabaseInitializer.
|
|
* @returns The singleton instance of the DatabaseInitializer.
|
|
*/
|
|
public static getInstance(): DatabaseInitializer {
|
|
if (!DatabaseInitializer.instance) {
|
|
DatabaseInitializer.instance = new DatabaseInitializer();
|
|
DatabaseInitializer.instance.initializationPromise = DatabaseInitializer.instance.setupDatabase();
|
|
}
|
|
return DatabaseInitializer.instance;
|
|
}
|
|
|
|
/**
|
|
* Retrieves the initialized database instance.
|
|
*
|
|
* @returns {Promise<Database | undefined>} The initialized database instance, or `undefined` if the
|
|
* database has not been initialized or if initialization failed.
|
|
*/
|
|
public async getDbInstance(): Promise<Database | undefined> {
|
|
if (this.initializationPromise) {
|
|
await this.initializationPromise; // Wait for the database setup to complete.
|
|
this.initializationPromise = undefined; // Clear promise after initial setup to avoid subsequent waits.
|
|
}
|
|
return this.db;
|
|
}
|
|
|
|
|
|
/**
|
|
* Initializes the SQLite database. If the database file does not exist, it will be created.
|
|
* @param dbPath The path to the SQLite database file.
|
|
* @returns A promise that resolves with the Database instance.
|
|
* @throws {Error} Throws an error if there's an issue opening the database.
|
|
*/
|
|
private async initializeDatabase(dbPath: string): Promise<Database> {
|
|
try {
|
|
const db = await open({
|
|
filename: dbPath,
|
|
driver: sqlite3.Database
|
|
});
|
|
console.log('Database initialized successfully.');
|
|
return db;
|
|
} catch (error) {
|
|
console.error('Failed to initialize the database:', error);
|
|
throw new Error('Failed to initialize the database.');
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Executes SQL files found within a specified folder. This function is designed to run migration and seed files.
|
|
* @param db The SQLite database instance.
|
|
* @param folderName The name of the folder containing the SQL files, relative to the class location.
|
|
* @throws {Error} Throws an error if there's an issue reading the directory or executing SQL files.
|
|
*/
|
|
private async runSqlFiles(db: Database, folderName: string): Promise<void> {
|
|
const folderPath = path.join(__dirname, '.', folderName); // Adjust for class location within src/db
|
|
|
|
console.log('folderPath', folderPath)
|
|
let files: string[];
|
|
try {
|
|
files = await fs.readdir(folderPath);
|
|
} catch (error) {
|
|
console.log(`Could not find or access the folder at ${folderPath}. Skipping execution of SQL files.`);
|
|
return; // Exit the function if the folder doesn't exist or can't be accessed
|
|
}
|
|
|
|
console.log('files', files)
|
|
|
|
const sqlFiles = files.filter(file => file.endsWith('.sql'));
|
|
|
|
if (sqlFiles.length === 0) {
|
|
console.log(`No SQL files found in ${folderName}. Skipping.`);
|
|
return;
|
|
}
|
|
|
|
for (const file of sqlFiles.sort()) {
|
|
const sql = await fs.readFile(path.join(folderPath, file), 'utf8');
|
|
await db.exec(sql);
|
|
console.log(`Executed ${file} in ${folderName}`);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* The main method for setting up the database. It runs the migrations first, then seeds the database.
|
|
* @example
|
|
* DatabaseInitializer.getInstance();
|
|
*/
|
|
private async setupDatabase(): Promise<void> {
|
|
const dbPath = path.join(__dirname, '.', 'appData.db');
|
|
this.db = await this.initializeDatabase(dbPath);
|
|
|
|
await this.runSqlFiles(this.db!, 'migrations');
|
|
await this.runSqlFiles(this.db!, 'seed');
|
|
|
|
}
|
|
}
|