UNPKG

@onurege3467/zerohelper

Version:

ZeroHelper is a versatile JavaScript library offering helper functions, validation, logging, database utilities and migration system for developers. It supports MongoDB, MySQL, SQLite, Redis, and PostgreSQL with increment/decrement operations.

228 lines (185 loc) 6.58 kB
const fs = require('fs'); const path = require('path'); class MigrationManager { constructor(database, options = {}) { this.db = database; this.migrationsDir = options.migrationsDir || './migrations'; this.migrationsTable = options.migrationsTable || 'migrations'; this.ensureMigrationsDir(); } ensureMigrationsDir() { if (!fs.existsSync(this.migrationsDir)) { fs.mkdirSync(this.migrationsDir, { recursive: true }); } } async ensureMigrationsTable() { try { // Migrations tablosu var mı kontrol et await this.db.selectOne(this.migrationsTable, {}); } catch (error) { // Tablo yoksa oluştur const createTableSQL = ` CREATE TABLE IF NOT EXISTS ${this.migrationsTable} ( id INTEGER PRIMARY KEY AUTOINCREMENT, name VARCHAR(255) NOT NULL UNIQUE, executed_at DATETIME DEFAULT CURRENT_TIMESTAMP ) `; // SQLite, MySQL, PostgreSQL için farklı SQL'ler if (this.db.constructor.name === 'MySQLDatabase') { await this.db.query(createTableSQL.replace('AUTOINCREMENT', 'AUTO_INCREMENT')); } else if (this.db.constructor.name === 'PostgreSQLDatabase') { const pgSQL = ` CREATE TABLE IF NOT EXISTS ${this.migrationsTable} ( id SERIAL PRIMARY KEY, name VARCHAR(255) NOT NULL UNIQUE, executed_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ) `; await this.db.query(pgSQL); } else { // SQLite await this.db.query(createTableSQL); } } } createMigration(name, description = '') { const timestamp = Date.now(); const filename = `${timestamp}_${name}.js`; const filePath = path.join(this.migrationsDir, filename); const template = `// Migration: ${name} // Description: ${description} // Created: ${new Date().toISOString()} module.exports = { async up(db) { // Migration işlemlerini buraya yazın // Örnek: // await db.query(\` // CREATE TABLE users ( // id INTEGER PRIMARY KEY AUTOINCREMENT, // name VARCHAR(255) NOT NULL, // email VARCHAR(255) UNIQUE NOT NULL // ) // \`); console.log('Migration ${name} executed (up)'); }, async down(db) { // Rollback işlemlerini buraya yazın // Örnek: // await db.query('DROP TABLE IF EXISTS users'); console.log('Migration ${name} rolled back (down)'); } }; `; fs.writeFileSync(filePath, template); console.log(`Migration oluşturuldu: ${filename}`); return filename; } getMigrationFiles() { const files = fs.readdirSync(this.migrationsDir) .filter(file => file.endsWith('.js')) .sort(); return files.map(file => ({ name: file, path: path.join(this.migrationsDir, file) })); } async getExecutedMigrations() { await this.ensureMigrationsTable(); try { const executed = await this.db.select(this.migrationsTable, {}); return executed.map(row => row.name); } catch (error) { console.error('Executed migrations alınırken hata:', error); return []; } } async getPendingMigrations() { const allMigrations = this.getMigrationFiles(); const executedMigrations = await this.getExecutedMigrations(); return allMigrations.filter(migration => !executedMigrations.includes(migration.name) ); } async runMigration(migrationFile, direction = 'up') { try { delete require.cache[require.resolve(migrationFile.path)]; const migration = require(migrationFile.path); if (typeof migration[direction] !== 'function') { throw new Error(`Migration ${migrationFile.name} has no ${direction} method`); } await migration[direction](this.db); if (direction === 'up') { await this.db.insert(this.migrationsTable, { name: migrationFile.name }); } else if (direction === 'down') { await this.db.delete(this.migrationsTable, { name: migrationFile.name }); } console.log(`✅ Migration ${migrationFile.name} (${direction}) başarılı`); return true; } catch (error) { console.error(`❌ Migration ${migrationFile.name} (${direction}) hatası:`, error); throw error; } } async migrate() { const pending = await this.getPendingMigrations(); if (pending.length === 0) { console.log('✅ Tüm migration\'lar güncel'); return; } console.log(`🔄 ${pending.length} migration çalıştırılacak...`); for (const migration of pending) { await this.runMigration(migration, 'up'); } console.log('✅ Tüm migration\'lar tamamlandı'); } async rollback(steps = 1) { const executed = await this.getExecutedMigrations(); if (executed.length === 0) { console.log('❌ Rollback yapılacak migration yok'); return; } const allMigrations = this.getMigrationFiles(); const toRollback = executed .slice(-steps) .reverse() .map(name => allMigrations.find(m => m.name === name)) .filter(Boolean); console.log(`🔄 ${toRollback.length} migration rollback edilecek...`); for (const migration of toRollback) { await this.runMigration(migration, 'down'); } console.log('✅ Rollback tamamlandı'); } async status() { const allMigrations = this.getMigrationFiles(); const executedMigrations = await this.getExecutedMigrations(); console.log('\n📋 Migration Durumu:'); console.log('=================='); if (allMigrations.length === 0) { console.log('❌ Migration dosyası bulunamadı'); return; } allMigrations.forEach(migration => { const status = executedMigrations.includes(migration.name) ? '✅ Executed' : '⏳ Pending'; console.log(`${status} ${migration.name}`); }); const pendingCount = allMigrations.length - executedMigrations.length; console.log(`\n📊 Toplam: ${allMigrations.length}, Executed: ${executedMigrations.length}, Pending: ${pendingCount}`); } async reset() { const executed = await this.getExecutedMigrations(); if (executed.length === 0) { console.log('❌ Reset edilecek migration yok'); return; } console.log(`🔄 ${executed.length} migration reset edilecek...`); await this.rollback(executed.length); console.log('✅ Tüm migration\'lar reset edildi'); } } module.exports = MigrationManager;