UNPKG

@onurege3467/zerohelper

Version:

ZeroHelper is a versatile high-performance utility library and database framework for Node.js, fully written in TypeScript.

335 lines (334 loc) • 15.4 kB
"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.dbCommand = void 0; const commander_1 = require("commander"); const chalk_1 = __importDefault(require("chalk")); const ora_1 = __importDefault(require("ora")); const fs_1 = __importDefault(require("fs")); const path_1 = __importDefault(require("path")); const index_1 = require("../../index"); const package_json_1 = require("../../package.json"); const config_1 = require("../utils/config"); const prompts_1 = require("../utils/prompts"); exports.dbCommand = new commander_1.Command().name('db'); exports.dbCommand .command('test') .description('Test database connection and show basic stats') .option('-c, --config <path>', 'Path to config file', 'zero.config.ts') .action(async (options) => { const spinner = (0, ora_1.default)('Testing database connection...').start(); try { const db = await (0, config_1.getDatabase)(options.config); spinner.succeed(chalk_1.default.green('āœ… Database connection successful')); console.log(chalk_1.default.bold('\nšŸ“Š Database Stats:')); console.log(chalk_1.default.cyan(' Adapter:'), chalk_1.default.white(db.constructor.name)); console.log(chalk_1.default.cyan(' Status:'), chalk_1.default.green('Connected')); try { const metrics = db.getMetrics(); console.log(chalk_1.default.cyan(' Operations:'), chalk_1.default.white(metrics.database.count)); console.log(chalk_1.default.cyan(' Avg Latency:'), chalk_1.default.white(`${metrics.database.averageDuration.toFixed(2)}ms`)); } catch (err) { console.log(chalk_1.default.yellow(' Note: Metrics not available for this adapter')); } await db.close(); } catch (error) { spinner.fail(chalk_1.default.red('āŒ Connection failed')); console.error(chalk_1.default.red(error.message)); process.exit(1); } }); exports.dbCommand .command('stats') .description('Show detailed database performance metrics') .option('-c, --config <path>', 'Path to config file', 'zero.config.ts') .action(async (options) => { try { const db = await (0, config_1.getDatabase)(options.config); const metrics = db.getMetrics(); console.log(chalk_1.default.bold('\nšŸ“Š Database Performance Dashboard')); console.log(chalk_1.default.gray('─'.repeat(50))); console.log(chalk_1.default.bold('\nšŸ”¹ Database Operations:')); if (metrics.database) { const count = metrics.database.count ?? 0; const totalDuration = metrics.database.totalDuration ?? 0; const avgLatency = metrics.database.averageDuration ?? 0; const minDuration = metrics.database.minDuration ?? 0; const maxDuration = metrics.database.maxDuration ?? 0; const formatNum = (n) => typeof n === 'number' ? n.toFixed(2) : n; console.log(` Total Operations: ${chalk_1.default.cyan(count)}`); console.log(` Total Duration: ${chalk_1.default.cyan(`${formatNum(totalDuration)} ms`)}`); console.log(` Avg Latency: ${chalk_1.default.green(`${formatNum(avgLatency)} ms`)}`); console.log(` Min Duration: ${chalk_1.default.yellow(`${formatNum(minDuration)} ms`)}`); console.log(` Max Duration: ${chalk_1.default.yellow(`${formatNum(maxDuration)} ms`)}`); } else { console.log(chalk_1.default.gray(' No metrics available for this adapter')); } if (metrics.cache) { console.log(chalk_1.default.bold('\nšŸ”¹ Cache Performance:')); console.log(` Total Requests: ${chalk_1.default.cyan((metrics.cache.hits || 0) + (metrics.cache.misses || 0))}`); console.log(` Cache Hits: ${chalk_1.default.green(metrics.cache.hits || 0)}`); console.log(` Cache Misses: ${chalk_1.default.red(metrics.cache.misses || 0)}`); const hits = metrics.cache.hits || 0; const misses = metrics.cache.misses || 0; const total = hits + misses; if (total > 0) { const ratio = (hits / total) * 100; console.log(` Hit Ratio: ${ratio.toFixed(1)}% ${ratio > 80 ? 'āœ…' : ratio > 50 ? 'āš ļø' : 'āŒ'}`); } } console.log(chalk_1.default.gray('\n' + '─'.repeat(50))); await db.close(); } catch (error) { console.error(chalk_1.default.red(`Error: ${error.message}`)); process.exit(1); } }); exports.dbCommand .command('seed') .description('Seed table with mock data') .option('-c, --config <path>', 'Path to config file', 'zero.config.ts') .option('-t, --table <name>', 'Table name') .option('-n, --count <number>', 'Number of records', '10') .action(async (options) => { if (!options.table) { console.error(chalk_1.default.red('Error: --table option is required')); process.exit(1); } const spinner = (0, ora_1.default)(`Seeding ${options.table}...`).start(); try { const db = await (0, config_1.getDatabase)(options.config); const seeder = new index_1.database.DataSeeder(db); const schema = {}; const fieldTypes = ['string', 'number', 'email', 'boolean', 'date']; for (let i = 0; i < 3; i++) { const fieldType = fieldTypes[Math.floor(Math.random() * fieldTypes.length)]; schema[`field_${i + 1}`] = { type: fieldType }; } const count = await seeder.seed(options.table, parseInt(options.count), schema); spinner.succeed(chalk_1.default.green(`āœ… Seeded ${count} records into ${options.table}`)); await db.close(); } catch (error) { spinner.fail(chalk_1.default.red('āŒ Seeding failed')); console.error(chalk_1.default.red(error.message)); process.exit(1); } }); exports.dbCommand .command('backup') .description('Backup database to timestamped file') .option('-c, --config <path>', 'Path to config file', 'zero.config.ts') .option('-o, --output <dir>', 'Output directory for backups', './backups') .action(async (options) => { const spinner = (0, ora_1.default)('Creating backup...').start(); try { const db = await (0, config_1.getDatabase)(options.config); const config = (0, config_1.loadConfig)(options.config); const backupDir = path_1.default.resolve(process.cwd(), options.output); if (!fs_1.default.existsSync(backupDir)) { fs_1.default.mkdirSync(backupDir, { recursive: true }); } const timestamp = new Date().toISOString().replace(/[:.]/g, '-').slice(0, -5); const backupFile = path_1.default.join(backupDir, `backup_${timestamp}.zerohelper.json`); let tables = []; if (config.adapter === 'json' && config.config.path) { const dbPath = path_1.default.resolve(process.cwd(), config.config.path); if (fs_1.default.existsSync(dbPath)) { const dbContent = JSON.parse(fs_1.default.readFileSync(dbPath, 'utf-8')); tables = Object.keys(dbContent); } } else if (config.adapter === 'sqlite' && config.config.path) { tables = await db.tables?.() || []; } else if (config.adapter === 'zpack' && config.config.path) { tables = await db.tables?.() || []; } else { tables = ['users', 'products', 'orders', 'migrations', 'migration_test', 'test_backup']; } const backupData = { version: package_json_1.version, timestamp: new Date().toISOString(), config: config, data: {} }; for (const table of tables) { try { const records = await db.select(table); if (records.length > 0) { backupData.data[table] = records; } } catch (err) { // Table doesn't exist or can't be accessed } } fs_1.default.writeFileSync(backupFile, JSON.stringify(backupData, null, 2)); const fileSize = fs_1.default.statSync(backupFile).size; spinner.succeed(chalk_1.default.green(`āœ… Backup created: ${backupFile}`)); console.log(chalk_1.default.gray(` Size: ${(0, config_1.formatBytes)(fileSize)}`)); console.log(chalk_1.default.gray(` Tables: ${Object.keys(backupData.data).join(', ') || 'none'}`)); await db.close(); } catch (error) { spinner.fail(chalk_1.default.red('āŒ Backup failed')); console.error(chalk_1.default.red(error.message)); process.exit(1); } }); exports.dbCommand .command('restore') .description('Restore database from backup file') .argument('<backup-file>', 'Path to backup file') .option('-c, --config <path>', 'Path to config file', 'zero.config.ts') .action(async (backupFile, options) => { if (!fs_1.default.existsSync(backupFile)) { console.error(chalk_1.default.red(`Error: Backup file not found: ${backupFile}`)); process.exit(1); } const backupData = JSON.parse(fs_1.default.readFileSync(backupFile, 'utf-8')); const confirmed = await (0, prompts_1.confirmAction)(chalk_1.default.yellow(`āš ļø This will restore data from backup. Are you sure?`)); if (!confirmed) { console.log(chalk_1.default.yellow('Restore cancelled')); return; } const spinner = (0, ora_1.default)('Restoring database...').start(); try { const db = await (0, config_1.getDatabase)(options.config); for (const [table, records] of Object.entries(backupData.data)) { const rows = records; if (Array.isArray(rows) && rows.length > 0) { try { await db.bulkInsert(table, rows); spinner.text = `āœ… ${table}: ${rows.length} records`; } catch (err) { spinner.text = `āš ļø ${table}: failed`; } } } spinner.succeed(chalk_1.default.green(`āœ… Database restored from ${backupFile}`)); await db.close(); } catch (error) { spinner.fail(chalk_1.default.red('āŒ Restore failed')); console.error(chalk_1.default.red(error.message)); process.exit(1); } }); exports.dbCommand .command('export') .description('Export table data to file') .option('-c, --config <path>', 'Path to config file', 'zero.config.ts') .option('-t, --table <name>', 'Table name to export') .option('-f, --format <format>', 'Output format (json|csv)', 'json') .option('-o, --output <file>', 'Output file path') .action(async (options) => { if (!options.table) { console.error(chalk_1.default.red('Error: --table option is required')); process.exit(1); } const spinner = (0, ora_1.default)(`Exporting ${options.table}...`).start(); try { const db = await (0, config_1.getDatabase)(options.config); const records = await db.select(options.table); if (records.length === 0) { spinner.warn(chalk_1.default.yellow(`āš ļø No records found in ${options.table}`)); await db.close(); return; } const timestamp = new Date().toISOString().replace(/[:.]/g, '-').slice(0, -5); const ext = options.format === 'csv' ? 'csv' : 'json'; const defaultFile = `${options.table}_export_${timestamp}.${ext}`; const outputFile = options.output || path_1.default.join(process.cwd(), 'exports', defaultFile); const outputDir = path_1.default.dirname(outputFile); if (!fs_1.default.existsSync(outputDir)) { fs_1.default.mkdirSync(outputDir, { recursive: true }); } let content = ''; if (options.format === 'csv') { const headers = Object.keys(records[0]); const csvRows = [ headers.join(','), ...records.map((row) => headers.map(h => { const val = row[h]; return typeof val === 'string' && val.includes(',') ? `"${val}"` : val; }).join(',')) ]; content = csvRows.join('\n'); } else { content = JSON.stringify(records, null, 2); } fs_1.default.writeFileSync(outputFile, content); const fileSize = fs_1.default.statSync(outputFile).size; spinner.succeed(chalk_1.default.green(`āœ… Exported ${records.length} records to ${outputFile}`)); console.log(chalk_1.default.gray(` Size: ${(0, config_1.formatBytes)(fileSize)}`)); console.log(chalk_1.default.gray(` Format: ${options.format.toUpperCase()}`)); await db.close(); } catch (error) { spinner.fail(chalk_1.default.red('āŒ Export failed')); console.error(chalk_1.default.red(error.message)); process.exit(1); } }); exports.dbCommand .command('import') .description('Import data from file to table') .argument('<file>', 'Input file path') .option('-c, --config <path>', 'Path to config file', 'zero.config.ts') .option('-t, --table <name>', 'Table name') .option('-f, --format <format>', 'Input format (json|csv)', 'json') .action(async (inputFile, options) => { if (!options.table) { console.error(chalk_1.default.red('Error: --table option is required')); process.exit(1); } if (!fs_1.default.existsSync(inputFile)) { console.error(chalk_1.default.red(`Error: File not found: ${inputFile}`)); process.exit(1); } const confirmed = await (0, prompts_1.confirmAction)(chalk_1.default.yellow(`āš ļø This will import data to ${options.table}. Are you sure?`)); if (!confirmed) { console.log(chalk_1.default.yellow('Import cancelled')); return; } const spinner = (0, ora_1.default)(`Importing to ${options.table}...`).start(); try { const db = await (0, config_1.getDatabase)(options.config); const content = fs_1.default.readFileSync(inputFile, 'utf-8'); let data = []; if (options.format === 'csv') { const lines = content.trim().split('\n'); const headers = lines[0].split(','); data = lines.slice(1).map((line) => { const values = line.split(','); const row = {}; headers.forEach((h, i) => { row[h] = values[i]?.replace(/"/g, '').trim(); }); return row; }); } else { data = JSON.parse(content); } const count = await db.bulkInsert(options.table, data); spinner.succeed(chalk_1.default.green(`āœ… Imported ${count} records to ${options.table}`)); await db.close(); } catch (error) { spinner.fail(chalk_1.default.red('āŒ Import failed')); console.error(chalk_1.default.red(error.message)); process.exit(1); } });