- 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
149 lines
4.9 KiB
JavaScript
Executable File
149 lines
4.9 KiB
JavaScript
Executable File
#!/usr/bin/env node
|
|
/**
|
|
* Setup database script
|
|
* Initializes the SQLite database with the schema
|
|
*/
|
|
|
|
const fs = require('fs');
|
|
const path = require('path');
|
|
const { initDatabase, getDatabase, closeDatabase } = require('../src/database/connection');
|
|
const { initEncryption } = require('../src/utils/encryption');
|
|
const { DB_PATH, KEY_FILE } = require('../src/database/config');
|
|
const crypto = require('crypto');
|
|
|
|
const DATABASE_PATH = DB_PATH;
|
|
const DATABASE_ENCRYPTION_KEY = process.env.DATABASE_ENCRYPTION_KEY;
|
|
const WAL_MODE = process.env.DATABASE_WAL_MODE !== '0' && process.env.DATABASE_WAL_MODE !== 'false';
|
|
const DATABASE_USE_SQLCIPHER = process.env.DATABASE_USE_SQLCIPHER !== '0' && process.env.DATABASE_USE_SQLCIPHER !== 'false';
|
|
|
|
async function setupDatabase() {
|
|
console.log('🔧 Setting up database...');
|
|
console.log(' Database path:', DATABASE_PATH);
|
|
|
|
// Ensure data directory exists
|
|
const dataDir = path.dirname(DATABASE_PATH);
|
|
if (!fs.existsSync(dataDir)) {
|
|
fs.mkdirSync(dataDir, { recursive: true });
|
|
console.log(' Created data directory:', dataDir);
|
|
}
|
|
|
|
let encryptionKey = DATABASE_ENCRYPTION_KEY;
|
|
|
|
// Check if encryption key is provided
|
|
if (!encryptionKey) {
|
|
if (fs.existsSync(KEY_FILE)) {
|
|
encryptionKey = fs.readFileSync(KEY_FILE, 'utf8').trim();
|
|
if (encryptionKey) {
|
|
process.env.DATABASE_ENCRYPTION_KEY = encryptionKey;
|
|
console.log('✅ Loaded encryption key from file');
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!encryptionKey) {
|
|
console.warn('⚠️ WARNING: No DATABASE_ENCRYPTION_KEY found!');
|
|
console.warn('⚠️ Generating a random key for this session (not persistent).');
|
|
console.warn('⚠️ For production, set DATABASE_ENCRYPTION_KEY environment variable.');
|
|
console.warn('⚠️ Generate one with: openssl rand -hex 32');
|
|
const generatedKey = crypto.randomBytes(32).toString('hex');
|
|
process.env.DATABASE_ENCRYPTION_KEY = generatedKey;
|
|
encryptionKey = generatedKey;
|
|
try {
|
|
const keyDir = path.dirname(KEY_FILE);
|
|
if (!fs.existsSync(keyDir)) fs.mkdirSync(keyDir, { recursive: true });
|
|
fs.writeFileSync(KEY_FILE, generatedKey, { mode: 0o600 });
|
|
console.log('⚠️ Saved generated key to:', KEY_FILE);
|
|
} catch (err) {
|
|
console.warn('⚠️ Failed to persist encryption key:', err.message);
|
|
}
|
|
console.log('✅ Generated temporary encryption key');
|
|
} else {
|
|
console.log('✅ Using encryption key from environment');
|
|
}
|
|
|
|
// Initialize encryption
|
|
try {
|
|
initEncryption(encryptionKey);
|
|
console.log('✅ Encryption initialized');
|
|
} catch (error) {
|
|
console.error('❌ Failed to initialize encryption:', error.message);
|
|
process.exit(1);
|
|
}
|
|
|
|
// Initialize database
|
|
try {
|
|
initDatabase(DATABASE_PATH, {
|
|
verbose: false,
|
|
walMode: WAL_MODE,
|
|
sqlcipherKey: DATABASE_USE_SQLCIPHER ? encryptionKey : null,
|
|
cipherCompatibility: process.env.DATABASE_CIPHER_COMPAT || 4,
|
|
kdfIter: process.env.DATABASE_KDF_ITER || 64000
|
|
});
|
|
console.log('✅ Database initialized');
|
|
} catch (error) {
|
|
console.error('❌ Failed to initialize database:', error.message);
|
|
process.exit(1);
|
|
}
|
|
|
|
// Load and execute schema
|
|
try {
|
|
const schemaPath = path.join(__dirname, '..', 'src', 'database', 'schema.sql');
|
|
const schema = fs.readFileSync(schemaPath, 'utf8');
|
|
|
|
const db = getDatabase();
|
|
|
|
// Execute the entire schema as one block
|
|
// SQLite can handle multiple statements with exec()
|
|
db.exec(schema);
|
|
|
|
// Add missing columns if this is an upgraded database
|
|
const userColumns = db.prepare('PRAGMA table_info(users)').all();
|
|
const userColumnNames = new Set(userColumns.map((c) => c.name));
|
|
if (!userColumnNames.has('data')) {
|
|
db.exec('ALTER TABLE users ADD COLUMN data TEXT');
|
|
console.log('✅ Added users.data column');
|
|
}
|
|
|
|
console.log('✅ Database schema created');
|
|
} catch (error) {
|
|
console.error('❌ Failed to create schema:', error.message);
|
|
closeDatabase();
|
|
process.exit(1);
|
|
}
|
|
|
|
// Verify tables
|
|
try {
|
|
const db = getDatabase();
|
|
const tables = db.prepare(`
|
|
SELECT name FROM sqlite_master
|
|
WHERE type='table' AND name NOT LIKE 'sqlite_%'
|
|
ORDER BY name
|
|
`).all();
|
|
|
|
console.log('✅ Database tables created:');
|
|
tables.forEach(table => {
|
|
console.log(` - ${table.name}`);
|
|
});
|
|
} catch (error) {
|
|
console.error('❌ Failed to verify tables:', error.message);
|
|
}
|
|
|
|
// Close database
|
|
closeDatabase();
|
|
|
|
console.log('');
|
|
console.log('✅ Database setup complete!');
|
|
console.log('');
|
|
console.log('Next steps:');
|
|
console.log(' 1. Run migration: node scripts/migrate-to-database.js');
|
|
console.log(' 2. Verify migration: node scripts/verify-migration.js');
|
|
console.log(' 3. Switch to database mode: unset USE_JSON_DATABASE');
|
|
console.log(' 4. Start server: npm start');
|
|
}
|
|
|
|
// Run setup
|
|
setupDatabase().catch(error => {
|
|
console.error('❌ Setup failed:', error);
|
|
process.exit(1);
|
|
});
|