Files
shopify-ai-backup/chat/src/database/connection.js
southseact-3d cb95a916ae Add database migration scripts and configuration files
- Add verify-migration.js script for testing database migrations
- Add database config module for centralized configuration
- Add chutes.txt prompt for system responses
- Update database implementation and testing documentation
- Add database migration and setup scripts
- Update session system and LLM tool configuration
- Update deployment checklist and environment example
- Update Dockerfile and docker-compose configuration
2026-02-20 12:38:43 +00:00

163 lines
3.5 KiB
JavaScript

/**
* Database connection module with SQLite support
* Uses better-sqlite3 for synchronous operations
* Note: SQLCipher support requires special compilation and is enabled via configuration
*/
const Database = require('better-sqlite3');
const path = require('path');
const fs = require('fs');
let db = null;
let dbPath = null;
function escapeSqliteString(value) {
return String(value || '').replace(/'/g, "''");
}
/**
* Initialize database connection
* @param {string} databasePath - Path to the database file
* @param {Object} options - Database options
* @returns {Database} Database instance
*/
function initDatabase(databasePath, options = {}) {
if (db) {
return db;
}
dbPath = databasePath;
// Ensure database directory exists
const dbDir = path.dirname(databasePath);
if (!fs.existsSync(dbDir)) {
fs.mkdirSync(dbDir, { recursive: true });
}
// Initialize database with options
const dbOptions = {
fileMustExist: false,
timeout: options.timeout || 5000,
};
// Add verbose if it's a function
if (options.verbose && typeof options.verbose === 'function') {
dbOptions.verbose = options.verbose;
}
db = new Database(databasePath, dbOptions);
// SQLCipher support (optional)
if (options.sqlcipherKey) {
const escapedKey = escapeSqliteString(options.sqlcipherKey);
db.pragma(`key = '${escapedKey}'`);
if (options.cipherCompatibility) {
db.pragma(`cipher_compatibility = ${Number(options.cipherCompatibility)}`);
}
if (options.kdfIter) {
db.pragma(`kdf_iter = ${Number(options.kdfIter)}`);
}
}
// Enable WAL mode for better concurrency
if (options.walMode !== false) {
db.pragma('journal_mode = WAL');
}
// Set reasonable defaults
db.pragma('synchronous = NORMAL');
db.pragma('cache_size = -64000'); // 64MB cache
db.pragma('temp_store = MEMORY');
db.pragma('foreign_keys = ON');
console.log('✅ Database connected:', databasePath);
return db;
}
/**
* Get database instance
* @returns {Database|null} Database instance or null if not initialized
*/
function getDatabase() {
return db;
}
/**
* Close database connection
*/
function closeDatabase() {
if (db) {
try {
db.close();
console.log('✅ Database connection closed');
} catch (error) {
console.error('Error closing database:', error);
} finally {
db = null;
dbPath = null;
}
}
}
/**
* Check if database is initialized
* @returns {boolean}
*/
function isDatabaseInitialized() {
return db !== null && db.open;
}
/**
* Get database path
* @returns {string|null}
*/
function getDatabasePath() {
return dbPath;
}
/**
* Create a backup of the database
* @param {string} backupPath - Path to backup file
* @returns {Promise<void>}
*/
async function backupDatabase(backupPath) {
if (!db) {
throw new Error('Database not initialized');
}
return new Promise((resolve, reject) => {
try {
const backup = db.backup(backupPath);
backup.step(-1); // Copy all pages at once
backup.finish();
console.log('✅ Database backup created:', backupPath);
resolve();
} catch (error) {
reject(error);
}
});
}
/**
* Execute a transaction
* @param {Function} fn - Function to execute in transaction
* @returns {*} Result of the function
*/
function transaction(fn) {
if (!db) {
throw new Error('Database not initialized');
}
return db.transaction(fn)();
}
module.exports = {
initDatabase,
getDatabase,
closeDatabase,
isDatabaseInitialized,
getDatabasePath,
backupDatabase,
transaction
};