fix(#14): Implement database migrations system with baseline migration
Some checks are pending
Docker Test / test (push) Waiting to run
Some checks are pending
Docker Test / test (push) Waiting to run
This commit is contained in:
parent
2f2ea4d483
commit
3916dd42bf
5 changed files with 248 additions and 23 deletions
114
backend/migrations/runner.js
Normal file
114
backend/migrations/runner.js
Normal file
|
|
@ -0,0 +1,114 @@
|
|||
import mysql from 'mysql2';
|
||||
import fs from 'fs/promises';
|
||||
import path from 'path';
|
||||
import { fileURLToPath } from 'url';
|
||||
import config from './config.js';
|
||||
|
||||
const __filename = fileURLToPath(import.meta.url);
|
||||
const __dirname = path.dirname(__filename);
|
||||
|
||||
// Create database connection
|
||||
const connection = mysql.createConnection(config.connection);
|
||||
|
||||
// Ensure migrations table exists
|
||||
function ensureMigrationsTable() {
|
||||
const createTableQuery = `
|
||||
CREATE TABLE IF NOT EXISTS ${config.tableName} (
|
||||
id INT AUTO_INCREMENT PRIMARY KEY,
|
||||
name VARCHAR(255) NOT NULL,
|
||||
executed_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
||||
)
|
||||
`;
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
connection.execute(createTableQuery, (err) => {
|
||||
if (err) reject(err);
|
||||
else resolve();
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
// Get list of already executed migrations
|
||||
function getExecutedMigrations() {
|
||||
const query = `SELECT name FROM ${config.tableName} ORDER BY executed_at`;
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
connection.execute(query, (err, results) => {
|
||||
if (err) reject(err);
|
||||
else resolve(results.map(row => row.name));
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
// Execute a migration file
|
||||
function executeMigration(migrationName, sqlContent) {
|
||||
return new Promise((resolve, reject) => {
|
||||
connection.execute(sqlContent, (err) => {
|
||||
if (err) reject(err);
|
||||
else {
|
||||
// Log the execution in migrations table
|
||||
const logQuery = `INSERT INTO ${config.tableName} (name) VALUES (?)`;
|
||||
connection.execute(logQuery, [migrationName], (logErr) => {
|
||||
if (logErr) reject(logErr);
|
||||
else resolve();
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
// Get all migration files
|
||||
async function getMigrationFiles() {
|
||||
try {
|
||||
const files = await fs.readdir(config.migrationsDir);
|
||||
return files
|
||||
.filter(file => file.endsWith('.sql'))
|
||||
.sort((a, b) => a.localeCompare(b));
|
||||
} catch (err) {
|
||||
if (err.code === 'ENOENT') {
|
||||
// Directory doesn't exist, create it
|
||||
await fs.mkdir(config.migrationsDir, { recursive: true });
|
||||
return [];
|
||||
}
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
|
||||
// Run migrations
|
||||
async function runMigrations() {
|
||||
try {
|
||||
await ensureMigrationsTable();
|
||||
const executed = await getExecutedMigrations();
|
||||
const allMigrations = await getMigrationFiles();
|
||||
|
||||
const pending = allMigrations.filter(name => !executed.includes(name));
|
||||
|
||||
if (pending.length === 0) {
|
||||
console.log('No pending migrations');
|
||||
return;
|
||||
}
|
||||
|
||||
console.log(`Running ${pending.length} migrations...`);
|
||||
|
||||
for (const migrationName of pending) {
|
||||
console.log(`Executing ${migrationName}`);
|
||||
const filePath = path.join(config.migrationsDir, migrationName);
|
||||
const sqlContent = await fs.readFile(filePath, 'utf8');
|
||||
await executeMigration(migrationName, sqlContent);
|
||||
}
|
||||
|
||||
console.log('All migrations executed successfully');
|
||||
} catch (err) {
|
||||
console.error('Error running migrations:', err);
|
||||
throw err;
|
||||
} finally {
|
||||
connection.end();
|
||||
}
|
||||
}
|
||||
|
||||
// Run the migrations
|
||||
if (process.argv.includes('--run')) {
|
||||
runMigrations().catch(console.error);
|
||||
}
|
||||
|
||||
export { runMigrations };
|
||||
Loading…
Add table
Add a link
Reference in a new issue