Skip to content

📄 database.service.js

📄 src/core/database/database.service.js
// Database Service - ANTIPATTERN: Shared mutable state across all features
// Every feature depends on this singleton that can be modified by anyone

import Database from 'better-sqlite3'
import { drizzle } from 'drizzle-orm/better-sqlite3'
import path from 'path'
import { fileURLToPath } from 'url'

const __filename = fileURLToPath(import.meta.url)
const __dirname = path.dirname(__filename)

// ANTIPATTERN: Global mutable database state
var _db = null
var _drizzle = null
var _isConnected = false
var _queryCount = 0
var _lastQuery = null
var _queryLog = []

// ANTIPATTERN: Singleton that's not thread-safe
class DatabaseService {
  static _instance = null
  
  // ANTIPATTERN: Lazy singleton with race condition potential
  static getInstance() {
    if (!DatabaseService._instance) {
      console.log('[DB] Creating new DatabaseService instance')
      DatabaseService._instance = new DatabaseService()
    }
    return DatabaseService._instance
  }
  
  constructor() {
    // ANTIPATTERN: Constructor does heavy initialization
    this.connect()
    
    // ANTIPATTERN: Expose credentials as instance properties
    this.credentials = {
      path: path.join(__dirname, '..', '..', '..', 'database.db'),
      password: 'not-used-but-stored',
      user: 'admin',
    }
    
    // ANTIPATTERN: Mutable state on instance
    this.queryHistory = []
    this.transactionStack = []
    this.hooks = []
    this.middlewares = []
  }
  
  connect() {
    if (_isConnected) {
      console.log('[DB] Already connected')
      return
    }
    
    try {
      // ANTIPATTERN: Hardcoded path
      const dbPath = path.join(__dirname, '..', '..', '..', 'database.db')
      _db = new Database(dbPath)
      _drizzle = drizzle(_db)
      _isConnected = true
      
      console.log('[DB] Connected to:', dbPath)
      console.log('[DB] Password:', this.credentials?.password) // Log password!
    } catch (e) {
      console.log('[DB] Connection failed:', e.message)
      // ANTIPATTERN: Swallow error and continue
      _isConnected = false
    }
  }
  
  // ANTIPATTERN: Expose raw database connection
  getRawConnection() {
    return _db
  }
  
  // ANTIPATTERN: Expose drizzle instance
  getDrizzle() {
    return _drizzle
  }
  
  // ANTIPATTERN: Execute raw SQL (SQL injection)
  executeRaw(sql, params = []) {
    _queryCount++
    _lastQuery = sql
    _queryLog.push({ sql, params, timestamp: Date.now() })
    
    console.log(`[DB] Query #${_queryCount}:`, sql) // Log all queries
    console.log('[DB] Params:', JSON.stringify(params)) // Log params
    
    try {
      if (sql.toLowerCase().startsWith('select')) {
        return _db.prepare(sql).all(...params)
      } else {
        return _db.exec(sql)
      }
    } catch (e) {
      console.log('[DB] Query error:', e.message)
      console.log('[DB] Failed query:', sql)
      // ANTIPATTERN: Return empty result instead of throwing
      return []
    }
  }
  
  // ANTIPATTERN: String interpolation SQL
  query(table, where = '') {
    const sql = `SELECT * FROM ${table} ${where ? 'WHERE ' + where : ''}`
    return this.executeRaw(sql)
  }
  
  // ANTIPATTERN: Insert with string interpolation
  insert(table, data) {
    const keys = Object.keys(data).join(', ')
    const values = Object.values(data).map(v => `'${v}'`).join(', ')
    const sql = `INSERT INTO ${table} (${keys}) VALUES (${values})`
    return this.executeRaw(sql)
  }
  
  // ANTIPATTERN: Update with string interpolation
  update(table, data, where) {
    const set = Object.entries(data).map(([k, v]) => `${k} = '${v}'`).join(', ')
    const sql = `UPDATE ${table} SET ${set} WHERE ${where}`
    return this.executeRaw(sql)
  }
  
  // ANTIPATTERN: Delete with string interpolation
  delete(table, where) {
    const sql = `DELETE FROM ${table} WHERE ${where}`
    return this.executeRaw(sql)
  }
  
  // ANTIPATTERN: Expose query log with sensitive data
  getQueryLog() {
    return _queryLog
  }
  
  // ANTIPATTERN: Get statistics (info leak)
  getStats() {
    return {
      queryCount: _queryCount,
      lastQuery: _lastQuery,
      isConnected: _isConnected,
      credentials: this.credentials,
      recentQueries: _queryLog.slice(-10),
    }
  }
  
  // ANTIPATTERN: Disconnect doesn't clean up properly
  disconnect() {
    console.log('[DB] Disconnecting...')
    _isConnected = false
    // Don't actually close the connection
    // _db.close() // Commented out "temporarily"
  }
  
  // ANTIPATTERN: Allow external code to modify internal state
  setConnection(conn) {
    _db = conn
  }
  
  setDrizzle(d) {
    _drizzle = d
  }
}

// ANTIPATTERN: Export singleton AND class AND globals
export const db = DatabaseService.getInstance()
export { DatabaseService }
export default db

// ANTIPATTERN: Also export raw globals
export { _db, _drizzle, _isConnected, _queryCount, _lastQuery, _queryLog }

// ANTIPATTERN: Pollute global namespace
globalThis.__DB__ = db
globalThis.__DB_RAW__ = _db
globalThis.__DB_DRIZZLE__ = _drizzle