- 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
163 lines
3.5 KiB
JavaScript
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
|
|
};
|