UNPKG

discord-media-server

Version:

Self-hosted Discord bot for scanning and serving information on local media files.

214 lines (182 loc) 6.47 kB
// lib/database.js (universal MySQL + SQLite handler) import dotenv from 'dotenv'; dotenv.config(); import fs from 'fs'; import path from 'path'; import { fileURLToPath } from 'url'; import { dirname } from 'path'; import { CONFIG_DIR, DATA_DIR } from './setupConfig.js'; let db; let pool; let isSQLite = false; const __filename = fileURLToPath(import.meta.url); const __dirname = dirname(__filename); // Lazy-loaded config & connection let initialized = false; /** Initializes DB connection based on config.json */ export async function initDatabase() { if (initialized) return; const sqlite3pkg = await import('sqlite3'); const sqlite3 = sqlite3pkg.default.verbose(); const { open } = await import('sqlite'); try { const dbType = process.env.DB_TYPE || 'sqlite'; isSQLite = dbType === 'sqlite'; if (isSQLite) { //const sqlite3 = (await import('better-sqlite3')).default; const dbFileName = path.basename(process.env.DB_FILE || 'media.sqlite'); const dataDir = DATA_DIR || path.resolve(CONFIG_DIR || __dirname, 'data'); const sqlitePath = path.resolve(dataDir, dbFileName); console.log("RUNTIME DB PATH:", sqlitePath); fs.mkdirSync(path.dirname(sqlitePath), { recursive: true }); //db = new sqlite3(sqlitePath); db = await open({ filename: sqlitePath, driver: sqlite3.Database }); if (!db) { throw new Error(`Failed to initialize SQLite DB at: ${sqlitePath}`); } //console.log(`SQLite DB loaded: ${sqlitePath}`); // Sanity check try { await db.all('SELECT 1'); //db.close(); console.log(`SQLite database ready at ${sqlitePath}`); } catch (err) { console.error('SQLite connection failed:', err.message); process.exit(1); } } else { const mysql = await import('mysql2/promise'); pool = mysql.createPool({ host: process.env.DB_HOST || 'localhost', user: process.env.DB_USER, password: process.env.DB_PASS, database: process.env.DB_NAME, waitForConnections: true, connectionLimit: 10, // ✅ adjustable queueLimit: 0 }); /* db = await mysql.createConnection({ host: process.env.DB_HOST || 'localhost', user: process.env.DB_USER, password: process.env.DB_PASS, database: process.env.DB_NAME }); if (!db || typeof db.prepare !== 'function') { throw new Error('Failed to initialize MySQL database.'); } //console.log(`MySQL DB connected: ${config.dbConfig.database || config.dbName}`); */ // Sanity check const [rows] = await pool.query('SELECT 1'); if (!rows) throw new Error('MySQL sanity check failed'); console.log('MySQL connection successful.'); } initialized = true; } catch(err) { console.log(`\ninitDatabase error:\nPlease run setup first.\n\n${err.message}`) } } /** Connects to SQLite for setup validation */ export async function connectSQLite(config) { const sqlite3 = await import('sqlite3'); const { open } = await import('sqlite'); //const sqlite3 = (await import('better-sqlite3')).default; const dbFileName = path.basename(process.env.DB_FILE || 'media.sqlite'); const dataDir = DATA_DIR || path.resolve(CONFIG_DIR || __dirname, 'data'); const dbPath = path.resolve(dataDir, dbFileName); // Ensure the folder exists fs.mkdirSync(path.dirname(dbPath), { recursive: true }); try { //const db = new sqlite3(dbPath); db = await open({ filename: dbPath, driver: sqlite3.Database }); await db.all('SELECT 1'); db.close(); console.log(`SQLite database ready at ${dbPath}`); } catch (err) { console.error('SQLite connection failed:', err.message); process.exit(1); } } /** Connects to MySQL for setup validation */ export async function connectMySQL(config) { try { const mysql = await import('mysql2/promise'); const db = await mysql.createConnection({ host: config.host, user: config.user, password: config.password, database: config.database }); await db.query('SELECT 1'); await db.end(); console.log('MySQL connection successful'); } catch (err) { console.error('MySQL connection failed:', err.message); process.exit(1); } } export async function query(sql, params = []) { if (!initialized) await initDatabase(); if (isSQLite) { if (!db) { throw new Error('Database is not initialized.'); } /* const stmt = await db.run(sql); if (sql.trim().toLowerCase().startsWith('select')) { return stmt.all(...params); } else { return stmt.run(...params); } */ const isSelect = sql.trim().toLowerCase().startsWith('select'); if (isSelect) { return db.all(sql, params); // return all rows } else { return db.run(sql, params); // insert/update/delete } } else { const [rows] = await pool.execute(sql, params); return rows; } } // lib/database.js export async function backupSQLite() { if (!initialized) await initDatabase(); if (!isSQLite || !db) return; const dbFileName = path.basename(process.env.DB_FILE || 'media.sqlite'); const dataDir = DATA_DIR || path.resolve(CONFIG_DIR || __dirname, 'data'); const backupName = dbFileName.replace('.sqlite', '') + `_${new Date().toISOString().replace(/[:.]/g, '-')}.sqlite`; const backupPath = path.resolve(dataDir, backupName); console.log(`\nCreating SQLite backup: ${backupPath}`); try { // USE db.db to access the raw sqlite3 driver const rawDb = db.db; await new Promise((resolve, reject) => { const backup = rawDb.backup(backupPath); backup.step(-1, (err) => { if (err) reject(err); }); backup.finish((err) => { if (err) reject(err); else resolve(); }); }); console.log(`SQLite backup completed: ${backupPath}`); return backupPath; } catch (err) { console.error("SQLite backup failed:", err.message); throw err; } } export async function close() { if (!initialized) return; if (isSQLite) { await db.close(); } else if (pool) { await pool.end(); } } export { isSQLite };