|
|
@@ -0,0 +1,494 @@
|
|
|
+const { executeQuery, executeNonQuery } = require('../config/database');
|
|
|
+const { logger } = require('./logger');
|
|
|
+const fs = require('fs');
|
|
|
+const path = require('path');
|
|
|
+
|
|
|
+class DatabaseInitializer {
|
|
|
+ constructor() {
|
|
|
+ this.initialized = false;
|
|
|
+ }
|
|
|
+
|
|
|
+ async initialize() {
|
|
|
+ if (this.initialized) {
|
|
|
+ logger.info('Database already initialized');
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ try {
|
|
|
+ logger.info('Starting database initialization...');
|
|
|
+
|
|
|
+ // Create tables based on database type
|
|
|
+ const dbType = process.env.DB_TYPE || 'sqlite';
|
|
|
+
|
|
|
+ switch (dbType) {
|
|
|
+ case 'mysql':
|
|
|
+ case 'postgres':
|
|
|
+ await this.initializeRelationalDatabase();
|
|
|
+ break;
|
|
|
+ case 'sqlite':
|
|
|
+ default:
|
|
|
+ await this.initializeSQLite();
|
|
|
+ break;
|
|
|
+ }
|
|
|
+
|
|
|
+ // Insert sample data
|
|
|
+ await this.insertSampleData();
|
|
|
+
|
|
|
+ this.initialized = true;
|
|
|
+ logger.info('Database initialization completed successfully');
|
|
|
+
|
|
|
+ } catch (error) {
|
|
|
+ logger.error('Database initialization failed:', error);
|
|
|
+ throw error;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ async initializeRelationalDatabase() {
|
|
|
+ try {
|
|
|
+ const dbType = process.env.DB_TYPE || 'mysql';
|
|
|
+
|
|
|
+ if (dbType === 'mysql') {
|
|
|
+ await this.initializeMySQL();
|
|
|
+ } else if (dbType === 'postgres') {
|
|
|
+ await this.initializePostgreSQL();
|
|
|
+ }
|
|
|
+
|
|
|
+ } catch (error) {
|
|
|
+ logger.error('Error creating relational database tables:', error);
|
|
|
+ throw error;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ async initializeMySQL() {
|
|
|
+ try {
|
|
|
+ // Create price_data table for MySQL
|
|
|
+ const createPriceTableQuery = `
|
|
|
+ CREATE TABLE IF NOT EXISTS price_data (
|
|
|
+ id INT PRIMARY KEY AUTO_INCREMENT,
|
|
|
+ symbol VARCHAR(10) NOT NULL,
|
|
|
+ timeframe VARCHAR(5) NOT NULL,
|
|
|
+ timestamp DATETIME NOT NULL,
|
|
|
+ open DECIMAL(15,8) NOT NULL,
|
|
|
+ high DECIMAL(15,8) NOT NULL,
|
|
|
+ low DECIMAL(15,8) NOT NULL,
|
|
|
+ close DECIMAL(15,8) NOT NULL,
|
|
|
+ volume BIGINT NOT NULL,
|
|
|
+ created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
|
|
+ INDEX idx_symbol_timeframe (symbol, timeframe),
|
|
|
+ INDEX idx_timestamp (timestamp),
|
|
|
+ INDEX idx_symbol_timestamp (symbol, timestamp)
|
|
|
+ ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
|
|
+ `;
|
|
|
+
|
|
|
+ await executeNonQuery(createPriceTableQuery);
|
|
|
+ logger.info('MySQL price data table created successfully');
|
|
|
+
|
|
|
+ // Create symbols table for MySQL
|
|
|
+ const createSymbolsTableQuery = `
|
|
|
+ CREATE TABLE IF NOT EXISTS symbols (
|
|
|
+ id INT PRIMARY KEY AUTO_INCREMENT,
|
|
|
+ symbol VARCHAR(10) UNIQUE NOT NULL,
|
|
|
+ name VARCHAR(100) NOT NULL,
|
|
|
+ category VARCHAR(20) NOT NULL,
|
|
|
+ exchange VARCHAR(20) NOT NULL,
|
|
|
+ is_active BOOLEAN DEFAULT TRUE,
|
|
|
+ created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
|
|
+ updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
|
|
|
+ INDEX idx_symbol (symbol),
|
|
|
+ INDEX idx_category (category),
|
|
|
+ INDEX idx_exchange (exchange)
|
|
|
+ ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
|
|
+ `;
|
|
|
+
|
|
|
+ await executeNonQuery(createSymbolsTableQuery);
|
|
|
+ logger.info('MySQL symbols table created successfully');
|
|
|
+
|
|
|
+ } catch (error) {
|
|
|
+ logger.error('Error creating MySQL tables:', error);
|
|
|
+ throw error;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ async initializePostgreSQL() {
|
|
|
+ try {
|
|
|
+ // Create price_data table for PostgreSQL
|
|
|
+ const createPriceTableQuery = `
|
|
|
+ CREATE TABLE IF NOT EXISTS price_data (
|
|
|
+ id SERIAL PRIMARY KEY,
|
|
|
+ symbol VARCHAR(10) NOT NULL,
|
|
|
+ timeframe VARCHAR(5) NOT NULL,
|
|
|
+ timestamp TIMESTAMP NOT NULL,
|
|
|
+ open DECIMAL(15,8) NOT NULL,
|
|
|
+ high DECIMAL(15,8) NOT NULL,
|
|
|
+ low DECIMAL(15,8) NOT NULL,
|
|
|
+ close DECIMAL(15,8) NOT NULL,
|
|
|
+ volume BIGINT NOT NULL,
|
|
|
+ created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
|
|
+ );
|
|
|
+ `;
|
|
|
+
|
|
|
+ await executeNonQuery(createPriceTableQuery);
|
|
|
+ logger.info('PostgreSQL price data table created successfully');
|
|
|
+
|
|
|
+ // Create indexes for PostgreSQL
|
|
|
+ const createIndexes = [
|
|
|
+ 'CREATE INDEX IF NOT EXISTS idx_symbol_timeframe ON price_data(symbol, timeframe);',
|
|
|
+ 'CREATE INDEX IF NOT EXISTS idx_timestamp ON price_data(timestamp);',
|
|
|
+ 'CREATE INDEX IF NOT EXISTS idx_symbol_timestamp ON price_data(symbol, timestamp);'
|
|
|
+ ];
|
|
|
+
|
|
|
+ for (const indexQuery of createIndexes) {
|
|
|
+ await executeNonQuery(indexQuery);
|
|
|
+ }
|
|
|
+
|
|
|
+ logger.info('PostgreSQL indexes created successfully');
|
|
|
+
|
|
|
+ // Create symbols table for PostgreSQL
|
|
|
+ const createSymbolsTableQuery = `
|
|
|
+ CREATE TABLE IF NOT EXISTS symbols (
|
|
|
+ id SERIAL PRIMARY KEY,
|
|
|
+ symbol VARCHAR(10) UNIQUE NOT NULL,
|
|
|
+ name VARCHAR(100) NOT NULL,
|
|
|
+ category VARCHAR(20) NOT NULL,
|
|
|
+ exchange VARCHAR(20) NOT NULL,
|
|
|
+ is_active BOOLEAN DEFAULT TRUE,
|
|
|
+ created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
|
|
+ updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
|
|
+ );
|
|
|
+ `;
|
|
|
+
|
|
|
+ await executeNonQuery(createSymbolsTableQuery);
|
|
|
+ logger.info('PostgreSQL symbols table created successfully');
|
|
|
+
|
|
|
+ } catch (error) {
|
|
|
+ logger.error('Error creating PostgreSQL tables:', error);
|
|
|
+ throw error;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ async initializeSQLite() {
|
|
|
+ try {
|
|
|
+ // Create price_data table
|
|
|
+ const createPriceTableQuery = `
|
|
|
+ CREATE TABLE IF NOT EXISTS price_data (
|
|
|
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
|
+ symbol TEXT NOT NULL,
|
|
|
+ timeframe TEXT NOT NULL,
|
|
|
+ timestamp DATETIME NOT NULL,
|
|
|
+ open REAL NOT NULL,
|
|
|
+ high REAL NOT NULL,
|
|
|
+ low REAL NOT NULL,
|
|
|
+ close REAL NOT NULL,
|
|
|
+ volume INTEGER NOT NULL,
|
|
|
+ created_at DATETIME DEFAULT CURRENT_TIMESTAMP
|
|
|
+ );
|
|
|
+ `;
|
|
|
+
|
|
|
+ await executeNonQuery(createPriceTableQuery);
|
|
|
+ logger.info('SQLite price data table created successfully');
|
|
|
+
|
|
|
+ // Create indexes for better performance
|
|
|
+ const createIndexes = [
|
|
|
+ 'CREATE INDEX IF NOT EXISTS idx_symbol_timeframe ON price_data(symbol, timeframe);',
|
|
|
+ 'CREATE INDEX IF NOT EXISTS idx_timestamp ON price_data(timestamp);',
|
|
|
+ 'CREATE INDEX IF NOT EXISTS idx_symbol_timestamp ON price_data(symbol, timestamp);'
|
|
|
+ ];
|
|
|
+
|
|
|
+ for (const indexQuery of createIndexes) {
|
|
|
+ await executeNonQuery(indexQuery);
|
|
|
+ }
|
|
|
+
|
|
|
+ logger.info('SQLite indexes created successfully');
|
|
|
+
|
|
|
+ // Create symbols table
|
|
|
+ const createSymbolsTableQuery = `
|
|
|
+ CREATE TABLE IF NOT EXISTS symbols (
|
|
|
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
|
+ symbol TEXT UNIQUE NOT NULL,
|
|
|
+ name TEXT NOT NULL,
|
|
|
+ category TEXT NOT NULL,
|
|
|
+ exchange TEXT NOT NULL,
|
|
|
+ is_active INTEGER DEFAULT 1,
|
|
|
+ created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
|
|
|
+ updated_at DATETIME DEFAULT CURRENT_TIMESTAMP
|
|
|
+ );
|
|
|
+ `;
|
|
|
+
|
|
|
+ await executeNonQuery(createSymbolsTableQuery);
|
|
|
+ logger.info('SQLite symbols table created successfully');
|
|
|
+
|
|
|
+ } catch (error) {
|
|
|
+ logger.error('Error creating SQLite tables:', error);
|
|
|
+ throw error;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ async insertSampleData() {
|
|
|
+ try {
|
|
|
+ const dbType = process.env.DB_TYPE || 'sqlite';
|
|
|
+
|
|
|
+ // Check if symbols already exist
|
|
|
+ let existingSymbols;
|
|
|
+ if (dbType === 'sqlite') {
|
|
|
+ existingSymbols = await executeQuery('SELECT COUNT(*) as count FROM symbols');
|
|
|
+ } else {
|
|
|
+ existingSymbols = await executeQuery('SELECT COUNT(*) as count FROM symbols');
|
|
|
+ }
|
|
|
+
|
|
|
+ // Handle different database result formats
|
|
|
+ let symbolCount = 0;
|
|
|
+ if (existingSymbols && existingSymbols.length > 0) {
|
|
|
+ symbolCount = existingSymbols[0].count || 0;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (symbolCount > 0) {
|
|
|
+ logger.info('Sample symbols already exist, skipping insertion');
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ // Insert sample symbols
|
|
|
+ const sampleSymbols = [
|
|
|
+ { symbol: 'EURUSD', name: 'Euro vs US Dollar', category: 'forex', exchange: 'FX' },
|
|
|
+ { symbol: 'GBPUSD', name: 'British Pound vs US Dollar', category: 'forex', exchange: 'FX' },
|
|
|
+ { symbol: 'USDJPY', name: 'US Dollar vs Japanese Yen', category: 'forex', exchange: 'FX' },
|
|
|
+ { symbol: 'USDCHF', name: 'US Dollar vs Swiss Franc', category: 'forex', exchange: 'FX' },
|
|
|
+ { symbol: 'USDCAD', name: 'US Dollar vs Canadian Dollar', category: 'forex', exchange: 'FX' },
|
|
|
+ { symbol: 'AUDUSD', name: 'Australian Dollar vs US Dollar', category: 'forex', exchange: 'FX' },
|
|
|
+ { symbol: 'NZDUSD', name: 'New Zealand Dollar vs US Dollar', category: 'forex', exchange: 'FX' },
|
|
|
+ { symbol: 'BTCUSD', name: 'Bitcoin vs US Dollar', category: 'crypto', exchange: 'Crypto' },
|
|
|
+ { symbol: 'ETHUSD', name: 'Ethereum vs US Dollar', category: 'crypto', exchange: 'Crypto' },
|
|
|
+ { symbol: 'AAPL', name: 'Apple Inc.', category: 'stocks', exchange: 'NASDAQ' },
|
|
|
+ { symbol: 'GOOGL', name: 'Alphabet Inc.', category: 'stocks', exchange: 'NASDAQ' },
|
|
|
+ { symbol: 'MSFT', name: 'Microsoft Corporation', category: 'stocks', exchange: 'NASDAQ' },
|
|
|
+ { symbol: 'TSLA', name: 'Tesla Inc.', category: 'stocks', exchange: 'NASDAQ' },
|
|
|
+ { symbol: 'AMZN', name: 'Amazon.com Inc.', category: 'stocks', exchange: 'NASDAQ' },
|
|
|
+ { symbol: 'META', name: 'Meta Platforms Inc.', category: 'stocks', exchange: 'NASDAQ' }
|
|
|
+ ];
|
|
|
+
|
|
|
+ for (const symbolData of sampleSymbols) {
|
|
|
+ let insertQuery;
|
|
|
+ if (dbType === 'sqlite') {
|
|
|
+ insertQuery = `
|
|
|
+ INSERT OR IGNORE INTO symbols (symbol, name, category, exchange, is_active)
|
|
|
+ VALUES (?, ?, ?, ?, 1)
|
|
|
+ `;
|
|
|
+ } else if (dbType === 'postgres') {
|
|
|
+ insertQuery = `
|
|
|
+ INSERT INTO symbols (symbol, name, category, exchange, is_active)
|
|
|
+ VALUES (?, ?, ?, ?, ?)
|
|
|
+ ON CONFLICT (symbol) DO NOTHING
|
|
|
+ `;
|
|
|
+ } else {
|
|
|
+ insertQuery = `
|
|
|
+ INSERT IGNORE INTO symbols (symbol, name, category, exchange, is_active)
|
|
|
+ VALUES (?, ?, ?, ?, 1)
|
|
|
+ `;
|
|
|
+ }
|
|
|
+
|
|
|
+ await executeNonQuery(insertQuery, [
|
|
|
+ symbolData.symbol,
|
|
|
+ symbolData.name,
|
|
|
+ symbolData.category,
|
|
|
+ symbolData.exchange,
|
|
|
+ true
|
|
|
+ ]);
|
|
|
+ }
|
|
|
+
|
|
|
+ logger.info(`Inserted ${sampleSymbols.length} sample symbols`);
|
|
|
+
|
|
|
+ // Generate sample price data for the last 1000 H1 candles
|
|
|
+ await this.generateSamplePriceData(sampleSymbols.slice(0, 7)); // Generate for first 7 symbols
|
|
|
+
|
|
|
+ } catch (error) {
|
|
|
+ logger.error('Error inserting sample data:', error);
|
|
|
+ throw error;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ async generateSamplePriceData(symbols) {
|
|
|
+ try {
|
|
|
+ logger.info('Generating sample price data...');
|
|
|
+
|
|
|
+ const now = new Date();
|
|
|
+ const dbType = process.env.DB_TYPE || 'sqlite';
|
|
|
+
|
|
|
+ for (const symbolData of symbols) {
|
|
|
+ logger.info(`Generating price data for ${symbolData.symbol}...`);
|
|
|
+
|
|
|
+ // Generate 1000 H1 candles (about 41 days of data)
|
|
|
+ const priceData = [];
|
|
|
+ let currentPrice = this.getInitialPrice(symbolData.symbol);
|
|
|
+
|
|
|
+ for (let i = 999; i >= 0; i--) {
|
|
|
+ const timestamp = new Date(now.getTime() - (i * 60 * 60 * 1000)); // H1 intervals
|
|
|
+ const open = currentPrice;
|
|
|
+
|
|
|
+ // Generate realistic price movement (±1%)
|
|
|
+ const change = (Math.random() - 0.5) * 0.02;
|
|
|
+ const close = open * (1 + change);
|
|
|
+
|
|
|
+ const high = Math.max(open, close) * (1 + Math.random() * 0.001);
|
|
|
+ const low = Math.min(open, close) * (1 - Math.random() * 0.001);
|
|
|
+ const volume = Math.floor(Math.random() * 1000000) + 100000;
|
|
|
+
|
|
|
+ priceData.push({
|
|
|
+ symbol: symbolData.symbol,
|
|
|
+ timeframe: 'H1',
|
|
|
+ timestamp,
|
|
|
+ open: open.toFixed(8),
|
|
|
+ high: high.toFixed(8),
|
|
|
+ low: low.toFixed(8),
|
|
|
+ close: close.toFixed(8),
|
|
|
+ volume
|
|
|
+ });
|
|
|
+
|
|
|
+ currentPrice = close;
|
|
|
+ }
|
|
|
+
|
|
|
+ // Insert price data in batches
|
|
|
+ const batchSize = 100;
|
|
|
+ for (let i = 0; i < priceData.length; i += batchSize) {
|
|
|
+ const batch = priceData.slice(i, i + batchSize);
|
|
|
+
|
|
|
+ for (const price of batch) {
|
|
|
+ let insertQuery;
|
|
|
+ if (dbType === 'sqlite') {
|
|
|
+ insertQuery = `
|
|
|
+ INSERT OR IGNORE INTO price_data (symbol, timeframe, timestamp, open, high, low, close, volume)
|
|
|
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?)
|
|
|
+ `;
|
|
|
+ } else if (dbType === 'postgres') {
|
|
|
+ insertQuery = `
|
|
|
+ INSERT INTO price_data (symbol, timeframe, timestamp, open, high, low, close, volume)
|
|
|
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?)
|
|
|
+ ON CONFLICT DO NOTHING
|
|
|
+ `;
|
|
|
+ } else {
|
|
|
+ insertQuery = `
|
|
|
+ INSERT IGNORE INTO price_data (symbol, timeframe, timestamp, open, high, low, close, volume)
|
|
|
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?)
|
|
|
+ `;
|
|
|
+ }
|
|
|
+
|
|
|
+ await executeNonQuery(insertQuery, [
|
|
|
+ price.symbol,
|
|
|
+ price.timeframe,
|
|
|
+ price.timestamp,
|
|
|
+ price.open,
|
|
|
+ price.high,
|
|
|
+ price.low,
|
|
|
+ price.close,
|
|
|
+ price.volume
|
|
|
+ ]);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ logger.info(`Generated ${priceData.length} price records for ${symbolData.symbol}`);
|
|
|
+ }
|
|
|
+
|
|
|
+ logger.info('Sample price data generation completed');
|
|
|
+
|
|
|
+ } catch (error) {
|
|
|
+ logger.error('Error generating sample price data:', error);
|
|
|
+ throw error;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ getInitialPrice(symbol) {
|
|
|
+ const initialPrices = {
|
|
|
+ 'EURUSD': 1.0850,
|
|
|
+ 'GBPUSD': 1.2650,
|
|
|
+ 'USDJPY': 150.50,
|
|
|
+ 'USDCHF': 0.9150,
|
|
|
+ 'USDCAD': 1.3450,
|
|
|
+ 'AUDUSD': 0.6550,
|
|
|
+ 'NZDUSD': 0.6050
|
|
|
+ };
|
|
|
+
|
|
|
+ return initialPrices[symbol] || 1.0000;
|
|
|
+ }
|
|
|
+
|
|
|
+ async reset() {
|
|
|
+ try {
|
|
|
+ logger.info('Resetting database...');
|
|
|
+
|
|
|
+ const dbType = process.env.DB_TYPE || 'sqlite';
|
|
|
+
|
|
|
+ // Drop tables
|
|
|
+ if (dbType === 'sqlite') {
|
|
|
+ await executeNonQuery('DROP TABLE IF EXISTS price_data');
|
|
|
+ await executeNonQuery('DROP TABLE IF EXISTS symbols');
|
|
|
+ } else {
|
|
|
+ await executeNonQuery('DROP TABLE IF EXISTS price_data');
|
|
|
+ await executeNonQuery('DROP TABLE IF EXISTS symbols');
|
|
|
+ }
|
|
|
+
|
|
|
+ this.initialized = false;
|
|
|
+ logger.info('Database reset completed');
|
|
|
+
|
|
|
+ // Reinitialize
|
|
|
+ await this.initialize();
|
|
|
+
|
|
|
+ } catch (error) {
|
|
|
+ logger.error('Error resetting database:', error);
|
|
|
+ throw error;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ async getStats() {
|
|
|
+ try {
|
|
|
+ const dbType = process.env.DB_TYPE || 'sqlite';
|
|
|
+
|
|
|
+ let symbolCount, priceCount;
|
|
|
+
|
|
|
+ if (dbType === 'sqlite') {
|
|
|
+ symbolCount = await executeQuery('SELECT COUNT(*) as count FROM symbols');
|
|
|
+ priceCount = await executeQuery('SELECT COUNT(*) as count FROM price_data');
|
|
|
+ } else {
|
|
|
+ symbolCount = await executeQuery('SELECT COUNT(*) as count FROM symbols');
|
|
|
+ priceCount = await executeQuery('SELECT COUNT(*) as count FROM price_data');
|
|
|
+ }
|
|
|
+
|
|
|
+ // Handle different database result formats
|
|
|
+ let symbols = 0;
|
|
|
+ let priceRecords = 0;
|
|
|
+
|
|
|
+ if (symbolCount && symbolCount.length > 0) {
|
|
|
+ symbols = symbolCount[0].count || 0;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (priceCount && priceCount.length > 0) {
|
|
|
+ priceRecords = priceCount[0].count || 0;
|
|
|
+ }
|
|
|
+
|
|
|
+ return {
|
|
|
+ symbols,
|
|
|
+ price_records: priceRecords,
|
|
|
+ initialized: this.initialized
|
|
|
+ };
|
|
|
+
|
|
|
+ } catch (error) {
|
|
|
+ logger.error('Error getting database stats:', error);
|
|
|
+ return {
|
|
|
+ symbols: 0,
|
|
|
+ price_records: 0,
|
|
|
+ initialized: false
|
|
|
+ };
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+const databaseInitializer = new DatabaseInitializer();
|
|
|
+
|
|
|
+// Export functions for external use
|
|
|
+const initializeDatabase = () => databaseInitializer.initialize();
|
|
|
+const resetDatabase = () => databaseInitializer.reset();
|
|
|
+const getDatabaseStats = () => databaseInitializer.getStats();
|
|
|
+
|
|
|
+module.exports = {
|
|
|
+ DatabaseInitializer,
|
|
|
+ initializeDatabase,
|
|
|
+ resetDatabase,
|
|
|
+ getDatabaseStats,
|
|
|
+ databaseInitializer
|
|
|
+};
|