const sqlite3 = require('sqlite3').verbose(); const path = require('path'); const fs = require('fs'); const logger = require('../utils/logger'); class LocalDatabase { constructor() { this.db = null; this.dbPath = null; } async initialize() { try { // Create data directory if it doesn't exist const dataDir = path.join(__dirname, '../../data'); if (!fs.existsSync(dataDir)) { fs.mkdirSync(dataDir, { recursive: true }); logger.info(`[LOCAL DB] Created data directory: ${dataDir}`); } this.dbPath = path.join(dataDir, 'mods.db'); logger.info(`[LOCAL DB] Initializing database at: ${this.dbPath}`); // Create database connection this.db = new sqlite3.Database(this.dbPath, (err) => { if (err) { logger.error('[LOCAL DB] Error opening database:', err.message); throw err; } else { logger.info('[LOCAL DB] Database connected successfully'); } }); // Enable foreign keys await this.run('PRAGMA foreign_keys = ON'); // Create tables await this.createTables(); logger.info('[LOCAL DB] Database initialized successfully'); return true; } catch (error) { logger.error('[LOCAL DB] Failed to initialize database:', error); throw error; } } async createTables() { const tables = [ // Mods table `CREATE TABLE IF NOT EXISTS mods ( id INTEGER PRIMARY KEY AUTOINCREMENT, name TEXT NOT NULL UNIQUE, version TEXT NOT NULL, author TEXT NOT NULL, description TEXT, enabled INTEGER DEFAULT 1, installed_at DATETIME DEFAULT CURRENT_TIMESTAMP, updated_at DATETIME DEFAULT CURRENT_TIMESTAMP, file_path TEXT NOT NULL, checksum TEXT, dependencies TEXT, config TEXT )`, // Mod assets table `CREATE TABLE IF NOT EXISTS mod_assets ( id INTEGER PRIMARY KEY AUTOINCREMENT, mod_id INTEGER NOT NULL, asset_type TEXT NOT NULL, -- 'ship', 'item', 'quest', etc. asset_id TEXT NOT NULL, asset_data TEXT NOT NULL, -- JSON data created_at DATETIME DEFAULT CURRENT_TIMESTAMP, FOREIGN KEY (mod_id) REFERENCES mods (id) ON DELETE CASCADE, UNIQUE(mod_id, asset_type, asset_id) )`, // Server mod preferences table `CREATE TABLE IF NOT EXISTS server_mod_preferences ( key TEXT PRIMARY KEY, value TEXT NOT NULL, updated_at DATETIME DEFAULT CURRENT_TIMESTAMP )`, // Mod load order table `CREATE TABLE IF NOT EXISTS mod_load_order ( id INTEGER PRIMARY KEY AUTOINCREMENT, mod_id INTEGER NOT NULL, load_order INTEGER NOT NULL, FOREIGN KEY (mod_id) REFERENCES mods (id) ON DELETE CASCADE, UNIQUE(mod_id) )` ]; for (const table of tables) { await this.run(table); } logger.info('[LOCAL DB] All tables created successfully'); } // Helper method to run SQL commands run(sql, params = []) { return new Promise((resolve, reject) => { this.db.run(sql, params, function(err) { if (err) { logger.error('[LOCAL DB] SQL Error:', err.message); reject(err); } else { resolve({ id: this.lastID, changes: this.changes }); } }); }); } // Helper method to get single row get(sql, params = []) { return new Promise((resolve, reject) => { this.db.get(sql, params, (err, row) => { if (err) { logger.error('[LOCAL DB] SQL Error:', err.message); reject(err); } else { resolve(row); } }); }); } // Helper method to get multiple rows all(sql, params = []) { return new Promise((resolve, reject) => { this.db.all(sql, params, (err, rows) => { if (err) { logger.error('[LOCAL DB] SQL Error:', err.message); reject(err); } else { resolve(rows); } }); }); } // Close database connection close() { return new Promise((resolve, reject) => { if (this.db) { this.db.close((err) => { if (err) { logger.error('[LOCAL DB] Error closing database:', err.message); reject(err); } else { logger.info('[LOCAL DB] Database closed'); resolve(); } }); } else { resolve(); } }); } // Get database instance getDatabase() { return this.db; } // Get database path getDatabasePath() { return this.dbPath; } } module.exports = new LocalDatabase();