UNPKG

hayai-db

Version:

⚡ Instantly create and manage local databases with one command

185 lines (184 loc) 7.02 kB
import { readFile, writeFile, access } from 'fs/promises'; import { constants } from 'fs'; import * as path from 'path'; import * as yaml from 'yaml'; import { getDockerManager } from './docker.js'; import { DatabaseTemplates } from './templates.js'; export class HayaiDbManager { static CONFIG_FILE = '.hayaidb'; static async exportConfig(outputPath) { const dockerManager = getDockerManager(); await dockerManager.initialize(); const instances = dockerManager.getAllInstances(); const configPath = outputPath || HayaiDbManager.CONFIG_FILE; const config = { version: '1.0', project: path.basename(process.cwd()), databases: {}, }; // Convert existing instances to database specs for (const instance of instances) { const spec = { engine: instance.engine, environment: instance.environment, }; // Add port only if it's allocated (not embedded databases) if (instance.port > 0) { spec.port = instance.port; } config.databases[instance.name] = spec; } // Generate YAML content const yamlContent = yaml.stringify(config, { indent: 2, lineWidth: 80, minContentWidth: 20, }); // Write to file await writeFile(configPath, yamlContent, 'utf-8'); return configPath; } static async syncConfig(configPath) { const filePath = configPath || HayaiDbManager.CONFIG_FILE; // Check if file exists if (!await HayaiDbManager.fileExists(filePath)) { throw new Error(`Configuration file '${filePath}' not found`); } // Read and parse config const content = await readFile(filePath, 'utf-8'); const config = yaml.parse(content); // Validate config HayaiDbManager.validateConfig(config); const dockerManager = getDockerManager(); await dockerManager.initialize(); const existingInstances = dockerManager.getAllInstances(); const existingNames = new Set(existingInstances.map(i => i.name)); const result = { created: [], skipped: [], errors: [], }; // Process each database in config for (const [name, spec] of Object.entries(config.databases)) { try { // Skip if already exists if (existingNames.has(name)) { result.skipped.push(name); continue; } // Get template for the engine const template = DatabaseTemplates.getTemplate(spec.engine); if (!template) { result.errors.push({ name, error: `Unknown engine: ${spec.engine}` }); continue; } // Create database instance await dockerManager.createDatabase(name, template, { port: spec.port, adminDashboard: spec.admin_dashboard, customEnv: spec.environment, }); result.created.push(name); } catch (error) { result.errors.push({ name, error: error instanceof Error ? error.message : String(error) }); } } return result; } static async validateConfig(config) { if (!config.version) { throw new Error('Missing required field: version'); } if (!config.databases || typeof config.databases !== 'object') { throw new Error('Missing or invalid field: databases'); } // Validate each database spec for (const [name, spec] of Object.entries(config.databases)) { if (!spec.engine) { throw new Error(`Database '${name}': missing required field 'engine'`); } // Check if engine is supported const template = DatabaseTemplates.getTemplate(spec.engine); if (!template) { throw new Error(`Database '${name}': unsupported engine '${spec.engine}'`); } // Validate port if specified if (spec.port !== undefined) { if (typeof spec.port !== 'number' || spec.port < 1 || spec.port > 65535) { throw new Error(`Database '${name}': invalid port ${spec.port}`); } } } } static async configExists(configPath) { const filePath = configPath || HayaiDbManager.CONFIG_FILE; return await HayaiDbManager.fileExists(filePath); } static async loadConfig(configPath) { const filePath = configPath || HayaiDbManager.CONFIG_FILE; if (!await HayaiDbManager.fileExists(filePath)) { throw new Error(`Configuration file '${filePath}' not found`); } const content = await readFile(filePath, 'utf-8'); const config = yaml.parse(content); await HayaiDbManager.validateConfig(config); return config; } static async fileExists(filePath) { try { await access(filePath, constants.F_OK); return true; } catch { return false; } } static generateSampleConfig() { return { version: '1.0', project: 'my-awesome-app', databases: { main_postgres: { engine: 'postgresql', port: 5432, environment: { POSTGRES_USER: 'myapp_user', POSTGRES_PASSWORD: 'secure_password_123', POSTGRES_DB: 'myapp_production', }, }, redis_cache: { engine: 'redis', port: 6379, environment: { REDIS_PASSWORD: 'redis_secret_456', }, }, metrics_influx: { engine: 'influxdb2', port: 8086, environment: { DOCKER_INFLUXDB_INIT_USERNAME: 'analytics_admin', DOCKER_INFLUXDB_INIT_PASSWORD: 'influx_pass_789', DOCKER_INFLUXDB_INIT_ORG: 'mycompany', DOCKER_INFLUXDB_INIT_BUCKET: 'app_metrics', }, }, dev_sqlite: { engine: 'sqlite', }, }, profiles: { development: ['main_postgres', 'redis_cache', 'dev_sqlite'], production: ['main_postgres', 'redis_cache', 'metrics_influx'], }, }; } }