Implement Phase 1.2: Database with encryption at rest and core infrastructure

Co-authored-by: southseact-3d <217551146+southseact-3d@users.noreply.github.com>
This commit is contained in:
copilot-swe-agent[bot]
2026-02-09 19:33:00 +00:00
parent 95a2d1b98d
commit 650d849ad2
17 changed files with 2716 additions and 0 deletions

View File

@@ -0,0 +1,143 @@
/**
* Database connection module with SQLite support
* Uses better-sqlite3 for synchronous operations
* Note: SQLCipher support requires special compilation, using AES-256-GCM encryption at field level instead
*/
const Database = require('better-sqlite3');
const path = require('path');
const fs = require('fs');
let db = null;
let dbPath = null;
/**
* 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 = {
verbose: options.verbose ? console.log : null,
fileMustExist: false,
timeout: options.timeout || 5000,
...options
};
db = new Database(databasePath, dbOptions);
// 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
};