@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
JavaScript
;
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);
}
});