UNPKG

tusktsk

Version:

TuskTsk - The Freedom Configuration Language. Query databases, use any syntax, never bow to any king!

258 lines (217 loc) 7.88 kB
/** * Database Commands for TuskLang CLI * =================================== * Implements all database operations from the CLI specification */ const { Command } = require('commander'); const fs = require('fs').promises; const path = require('path'); const TuskLang = require('../../index.js'); // Database adapters const adapters = { sqlite: require('../../adapters/sqlite.js'), postgres: require('../../adapters/postgres.js'), mysql: require('../../adapters/mysql.js'), mongodb: require('../../adapters/mongodb.js'), redis: require('../../adapters/redis.js') }; // Helper function to get database adapter async function getDatabaseAdapter() { try { // Try to load from peanut.tsk const peanutConfig = new (require('../../peanut-config.js'))(); const config = peanutConfig.load(process.cwd()); if (config.database) { const { type, ...options } = config.database; if (adapters[type]) { return new adapters[type](options); } } // Fallback to SQLite return new adapters.sqlite({ filename: ':memory:' }); } catch (error) { console.warn('⚠️ No database configuration found, using SQLite in-memory'); return new adapters.sqlite({ filename: ':memory:' }); } } // Database status command const status = new Command('status') .description('Check database connection status') .action(async () => { try { console.log('🔄 Checking database connection...'); const adapter = await getDatabaseAdapter(); const isConnected = await adapter.isConnected(); if (isConnected) { console.log('✅ Database connected successfully'); return { status: 'connected', adapter: adapter.constructor.name }; } else { console.log('❌ Database connection failed'); return { status: 'disconnected', adapter: adapter.constructor.name }; } } catch (error) { console.error('❌ Database status check failed:', error.message); return { status: 'error', error: error.message }; } }); // Database migrate command const migrate = new Command('migrate') .description('Run migration file') .argument('<file>', 'Migration file to run') .action(async (file) => { try { console.log(`🔄 Running migration: ${file}`); // Check if file exists await fs.access(file); // Read migration file const migrationContent = await fs.readFile(file, 'utf8'); // Get database adapter const adapter = await getDatabaseAdapter(); // Execute migration const result = await adapter.query(migrationContent); console.log('✅ Migration completed successfully'); return { success: true, file, result }; } catch (error) { console.error('❌ Migration failed:', error.message); return { success: false, file, error: error.message }; } }); // Database console command const console = new Command('console') .description('Open interactive database console') .action(async () => { try { console.log('🔄 Opening database console...'); const adapter = await getDatabaseAdapter(); console.log(`📍 Connected to ${adapter.constructor.name}`); console.log('Type SQL queries, "exit" to quit\n'); const readline = require('readline'); const rl = readline.createInterface({ input: process.stdin, output: process.stdout, prompt: 'db> ' }); rl.prompt(); rl.on('line', async (line) => { const input = line.trim(); if (input === 'exit' || input === 'quit') { rl.close(); return; } if (input) { try { const result = await adapter.query(input); console.log('Result:', result); } catch (error) { console.error('❌ Query error:', error.message); } } rl.prompt(); }); rl.on('close', () => { console.log('Database console closed'); process.exit(0); }); } catch (error) { console.error('❌ Failed to open database console:', error.message); return { success: false, error: error.message }; } }); // Database backup command const backup = new Command('backup') .description('Backup database') .argument('[file]', 'Backup file name', `tusklang_backup_${Date.now()}.sql`) .action(async (file) => { try { console.log(`🔄 Creating database backup: ${file}`); const adapter = await getDatabaseAdapter(); // For SQLite, copy the file if (adapter.constructor.name === 'SQLiteAdapter') { if (adapter.filename !== ':memory:') { await fs.copyFile(adapter.filename, file); console.log('✅ SQLite database backed up successfully'); return { success: true, file }; } else { throw new Error('Cannot backup in-memory SQLite database'); } } // For other databases, export schema and data const schema = await adapter.query("SELECT sql FROM sqlite_master WHERE type='table'"); const backupContent = schema.map(row => row.sql).join(';\n\n'); await fs.writeFile(file, backupContent); console.log('✅ Database backup created successfully'); return { success: true, file }; } catch (error) { console.error('❌ Backup failed:', error.message); return { success: false, file, error: error.message }; } }); // Database restore command const restore = new Command('restore') .description('Restore from backup file') .argument('<file>', 'Backup file to restore from') .action(async (file) => { try { console.log(`🔄 Restoring database from: ${file}`); // Check if backup file exists await fs.access(file); // Read backup file const backupContent = await fs.readFile(file, 'utf8'); // Get database adapter const adapter = await getDatabaseAdapter(); // Execute restore const result = await adapter.query(backupContent); console.log('✅ Database restored successfully'); return { success: true, file, result }; } catch (error) { console.error('❌ Restore failed:', error.message); return { success: false, file, error: error.message }; } }); // Database init command const init = new Command('init') .description('Initialize SQLite database') .action(async () => { try { console.log('🔄 Initializing SQLite database...'); const dbPath = path.join(process.cwd(), 'tusklang.db'); const adapter = new adapters.sqlite({ filename: dbPath }); // Create basic tables await adapter.query(` CREATE TABLE IF NOT EXISTS users ( id INTEGER PRIMARY KEY, name TEXT NOT NULL, email TEXT UNIQUE, created_at DATETIME DEFAULT CURRENT_TIMESTAMP ) `); await adapter.query(` CREATE TABLE IF NOT EXISTS settings ( key TEXT PRIMARY KEY, value TEXT, type TEXT DEFAULT 'string' ) `); await adapter.query(` CREATE TABLE IF NOT EXISTS migrations ( id INTEGER PRIMARY KEY, name TEXT NOT NULL, executed_at DATETIME DEFAULT CURRENT_TIMESTAMP ) `); console.log('✅ SQLite database initialized successfully'); console.log(`📍 Database file: ${dbPath}`); return { success: true, database: dbPath }; } catch (error) { console.error('❌ Database initialization failed:', error.message); return { success: false, error: error.message }; } }); module.exports = { status, migrate, console, backup, restore, init };