mysql-migration 1.0.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md ADDED
@@ -0,0 +1,47 @@
1
+ # MySQL Migration Database
2
+
3
+ A simple command-line tool for generating and running database migrations for MySQL.
4
+
5
+ ## Installation
6
+
7
+ You can install `mysql-migration` using npm:
8
+ npm install -g mysql-migration
9
+
10
+
11
+ ## Usage
12
+
13
+ ### Create a new migration
14
+
15
+ To create a new migration, run the following command:
16
+ npx mysql-migration migration-name database-name
17
+
18
+ This will create a new migration file in the `migrations` directory with a timestamp and the name `migration-name`.
19
+
20
+
21
+ ### Running migrations
22
+
23
+ To run pending migrations, run the following command:
24
+ npx mysql-migration run database-name (optional)
25
+
26
+
27
+ This will run all migrations that have not yet been run.
28
+
29
+ ### Initialize the migrations table
30
+
31
+ If you have not yet run any migrations, you need to initialize the migrations table by running the following command:
32
+ npx mysql-migration init
33
+
34
+
35
+ ### Configuration
36
+
37
+ The configuration file should export an object with the following properties:
38
+
39
+ - `host`: the MySQL server host
40
+ - `database`: the name of the database to migrate
41
+ - `user`: the MySQL user name
42
+ - `password`: the MySQL user password
43
+
44
+
45
+ ### License
46
+
47
+ This package is licensed under the MIT License.
package/index.js ADDED
@@ -0,0 +1,23 @@
1
+ #!/usr/bin/env node
2
+ const program = require('commander');
3
+ //---------------------------------------
4
+ program
5
+ .version('1.0.0')
6
+ .description('Migration for mysql database');
7
+ //---------------------------------------
8
+ program
9
+ .command('init')
10
+ .description('Initialize migration')
11
+ .action(() => require('./src/commands/init'));
12
+ //---------------------------------------
13
+ program
14
+ .command('run [dbName]')
15
+ .description('Run migration')
16
+ .action((dbName) => require('./src/commands/run')(dbName));
17
+ //---------------------------------------
18
+ program
19
+ .command('create <migrationName> <dbName>')
20
+ .description('Create a new migration')
21
+ .action((migrationName, dbName) => require('./src/commands/create')(migrationName, dbName));
22
+ //---------------------------------------
23
+ program.parse(process.argv);
package/package.json ADDED
@@ -0,0 +1,25 @@
1
+ {
2
+ "name": "mysql-migration",
3
+ "version": "1.0.5",
4
+ "description": "Migration for mysql database",
5
+ "main": "index.js",
6
+ "bin": {
7
+ "mysql-migration": "./index.js"
8
+ },
9
+ "scripts": {
10
+ "test": "echo \"Error: no test specified\" && exit 1"
11
+ },
12
+ "keywords": [
13
+ "mysql migration",
14
+ "migration",
15
+ "mysql"
16
+ ],
17
+ "dependencies": {
18
+ "mysql": "^2.18.1",
19
+ "moment": "^2.24.0",
20
+ "fs": "0.0.1-security",
21
+ "commander": "^2.20.0"
22
+ },
23
+ "author": "SherKan",
24
+ "license": "MIT"
25
+ }
@@ -0,0 +1,60 @@
1
+ 'use strict';
2
+ //---------------------------------------
3
+ const fs = require('fs');
4
+ const moment = require('moment');
5
+ //---------------------------------------
6
+ const currentPath = process.cwd();
7
+ const config = require(`${currentPath}/migrations/config.json`);
8
+ //---------------------------------------
9
+ function create_migration(migrationName, dbName) {
10
+ if (!migrationName) {
11
+ console.error('\x1b[31m%s\x1b[0m', `Error: Migration name is empty !`);
12
+ process.exit(1);
13
+ }
14
+ //---------------------------------------
15
+ const databases = Object.keys(config.databases);
16
+ //---------------------------------------
17
+ if (!databases.includes(dbName)) {
18
+ console.error('\x1b[31m%s\x1b[0m', `Error: Invalid database name "${dbName}" can be: ${databases.join(', ')}.`);
19
+ process.exit(1);
20
+ }
21
+ //---------------------------------------
22
+ if (!fs.existsSync(`${currentPath}/migrations/${dbName}_db`)) fs.mkdirSync(`${currentPath}/migrations/${dbName}_db`);
23
+ //---------------------------------------
24
+ const currentDate = moment().format('YYYY_MM_DD_HHmmss');
25
+ const fileName = `${currentDate}_${migrationName}.js`;
26
+ const filePath = `${currentPath}/migrations/${dbName}_db/${fileName}`;
27
+ //---------------------------------------
28
+ if (fs.existsSync(filePath)) console.warn('\x1b[33m%s\x1b[0m', `Warning: File "${fileName}" already exists in the "migrations" directory.`);
29
+ else {
30
+ const dataText = `module.exports = {
31
+ up: (connection) => {
32
+ const query = "";
33
+
34
+ return new Promise((resolve, reject) => {
35
+ if (!query) reject('Migration query is empty !');
36
+ connection.query(query, (err) => {
37
+ if (err) reject(err);
38
+ resolve();
39
+ });
40
+ });
41
+ },
42
+
43
+ down: (connection) => {
44
+ const query = "";
45
+
46
+ return new Promise((resolve, reject) => {
47
+ if (!query) reject('Migration query is empty !');
48
+ connection.query(query, (err) => {
49
+ if (err) reject(err);
50
+ resolve();
51
+ });
52
+ });
53
+ }
54
+ };`
55
+ fs.writeFileSync(filePath, dataText);
56
+ console.log('\x1b[32m%s\x1b[0m', `Created migration file: "${fileName}".`);
57
+ }
58
+ }
59
+ //---------------------------------------
60
+ module.exports = create_migration;
@@ -0,0 +1,18 @@
1
+ 'use strict';
2
+ //---------------------------------------
3
+ const fs = require('fs');
4
+ //---------------------------------------
5
+ const currentPath = process.cwd();
6
+ //---------------------------------------
7
+ if (!fs.existsSync(`${currentPath}/migrations`)) fs.mkdirSync(`${currentPath}/migrations`);
8
+ if (!fs.existsSync(`${currentPath}/migrations/config.json`)) fs.writeFileSync(`${currentPath}/migrations/config.json`, JSON.stringify({
9
+ "databases": {
10
+ "db_name": {
11
+ "host": "db_host",
12
+ "user": "db_user",
13
+ "password": "db_password",
14
+ "database": "db_name"
15
+ }
16
+ }
17
+ }, null, 3));
18
+ console.log('\x1b[32m%s\x1b[0m', `Created migration config file: "config.json" in the "migrations" directory.`);
@@ -0,0 +1,103 @@
1
+ 'use strict';
2
+ //==============================================================================
3
+ const fs = require('fs');
4
+ const mysql = require('mysql');
5
+ //---------------------------------------
6
+ const currentPath = process.cwd();
7
+ const config = require(`${currentPath}/migrations/config.json`);
8
+ const { checkTableMigrations, createTableMigrations, getAllMigrations, getCurrentBatch, insertMigration } = require("../utils/functions");
9
+ //==============================================================================
10
+ async function run_migration(dbName) {
11
+ const connection = {}, migrations = [];
12
+ const databases = config.databases;
13
+ //---------------------------------------
14
+ if (dbName) {
15
+ if (!databases[dbName]) {
16
+ console.error('\x1b[31m%s\x1b[0m', `Error: Invalid database name "${dbName}".`);
17
+ process.exit(1);
18
+ }
19
+ //---------------------------------------
20
+ connection[dbName] = mysql.createConnection(databases[dbName]);
21
+ connection[dbName].connect((err) => {
22
+ if (err) {
23
+ console.error('\x1b[31m%s\x1b[0m', `Error: Unable to connect to database "${dbName}".\n${err}`);
24
+ process.exit(1);
25
+ }
26
+ });
27
+ //---------------------------------------
28
+ const tableMigrations = await checkTableMigrations(connection[dbName]);
29
+ if (!tableMigrations) await createTableMigrations(connection[dbName]);
30
+ //---------------------------------------
31
+ const batch = await getCurrentBatch(connection[dbName]) + 1;
32
+ const allMigrations = await getAllMigrations(connection[dbName]);
33
+ const migration = fs.readdirSync(`${currentPath}/migrations/${dbName}_db`);
34
+ //---------------------------------------
35
+ const migrations = migration.filter(val => !allMigrations.some(val2 => val.includes(val2.migration)));
36
+ //---------------------------------------
37
+ if (migrations.length === 0) {
38
+ console.log('\x1b[32m%s\x1b[0m', 'Nothing to migrate.\n');
39
+ connection[dbName].end();
40
+ process.exit(1);
41
+ }
42
+ //---------------------------------------
43
+ for (let file of migrations) {
44
+ const migration = require(`${currentPath}/migrations/${dbName}_db/${file}`);
45
+ try {
46
+ await migration.up(connection[dbName]);
47
+ await insertMigration(connection[dbName], file.replace('.js', ''), batch);
48
+ console.log('\x1b[36m%s\x1b[0m', `Migrated: "${file}" successfully.`);
49
+ }
50
+ catch (err) {
51
+ console.warn('\x1b[33m%s\x1b[0m', `Warning: "${err}" in migration "${file}".`);
52
+ }
53
+ }
54
+ console.log('\x1b[32m%s\x1b[0m', 'All migrations have been completed successfully.\n');
55
+ connection[dbName].end();
56
+ }
57
+ else {
58
+ for (let key in databases) {
59
+ connection[key] = mysql.createConnection(databases[key]);
60
+ connection[key].connect((err) => {
61
+ if (err) {
62
+ console.error('\x1b[31m%s\x1b[0m', `Error: Unable to connect to database "${key}".`);
63
+ process.exit(1);
64
+ }
65
+ });
66
+ //---------------------------------------
67
+ const tableMigrations = await checkTableMigrations(connection[key]);
68
+ if (!tableMigrations) await createTableMigrations(connection[key]);
69
+ //---------------------------------------
70
+ const batch = await getCurrentBatch(connection[key]) + 1;
71
+ const allMigrations = await getAllMigrations(connection[key]);
72
+ //---------------------------------------
73
+ if (!fs.existsSync(`${currentPath}/migrations/${key}_db`)) continue;
74
+ const migration = fs.readdirSync(`${currentPath}/migrations/${key}_db`);
75
+ //---------------------------------------
76
+ const diffMigrations = migration.filter(val => !allMigrations.some(val2 => val.includes(val2.migration)));
77
+ //---------------------------------------
78
+ for (let m of diffMigrations) migrations.push([m, key, batch]);
79
+ }
80
+ //---------------------------------------
81
+ if (migrations.length === 0) {
82
+ console.log('\x1b[32m%s\x1b[0m', 'Nothing to migrate.\n');
83
+ for (let key in connection) connection[key].end();
84
+ process.exit(1);
85
+ }
86
+ //---------------------------------------
87
+ for (let [file, key, batch] of migrations) {
88
+ const migration = require(`${currentPath}/migrations/${key}_db/${file}`);
89
+ try {
90
+ await migration.up(connection[key]);
91
+ await insertMigration(connection[key], file.replace('.js', ''), batch);
92
+ console.log('\x1b[36m%s\x1b[0m', `Migrated: "${file}" in database "${key}" successfully.`);
93
+ }
94
+ catch (err) {
95
+ console.warn('\x1b[33m%s\x1b[0m', `Warning: "${err}" in migration "${file}" in database "${key}".`);
96
+ }
97
+ }
98
+ console.log('\x1b[32m%s\x1b[0m', 'All migrations have been completed successfully.\n');
99
+ for (let key in connection) connection[key].end();
100
+ }
101
+ }
102
+ //==============================================================================
103
+ module.exports = run_migration;
@@ -0,0 +1,57 @@
1
+ function checkTableMigrations(connection) {
2
+ return new Promise((resolve) => {
3
+ connection.query("SELECT `id` FROM `migrations` LIMIT 1;", (error) => {
4
+ if (error) resolve(false);
5
+ else resolve(true);
6
+ });
7
+ });
8
+ }
9
+ //---------------------------------------
10
+ function createTableMigrations(connection) {
11
+ return new Promise((resolve) => {
12
+ const query = "CREATE TABLE `migrations` (\
13
+ `id` INT NOT NULL AUTO_INCREMENT,\
14
+ `migration` VARCHAR(255) NOT NULL,\
15
+ `batch` INT NOT NULL DEFAULT 1,\
16
+ `created_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,\
17
+ PRIMARY KEY (`id`));"
18
+ connection.query(query, (error) => {
19
+ if (error) throw error;
20
+ resolve(true);
21
+ });
22
+ });
23
+ }
24
+ //---------------------------------------
25
+ function getAllMigrations(connection) {
26
+ return new Promise((resolve) => {
27
+ connection.query("SELECT `migration` FROM `migrations`;", (error, results) => {
28
+ if (error) throw error;
29
+ if (results?.length > 0) resolve(results);
30
+ else resolve([]);
31
+ });
32
+ });
33
+ }
34
+ //---------------------------------------
35
+ function getCurrentBatch(connection) {
36
+ return new Promise((resolve) => {
37
+ connection.query("SELECT `batch` FROM `migrations` ORDER BY `batch` DESC LIMIT 1;", (error, results) => {
38
+ if (error) throw error;
39
+ if (results?.length > 0) resolve(results[0].batch);
40
+ else resolve(0);
41
+ });
42
+ });
43
+ }
44
+ //---------------------------------------
45
+ function insertMigration(connection, migration, batch) {
46
+ return new Promise((resolve) => {
47
+ const query = `INSERT INTO \`migrations\` (\`migration\`, \`batch\`) VALUES ('${migration}', '${batch}');`
48
+ connection.query(query, (error) => {
49
+ if (error) throw error;
50
+ resolve(true);
51
+ });
52
+ });
53
+ }
54
+ //---------------------------------------
55
+ module.exports = {
56
+ checkTableMigrations, createTableMigrations, getAllMigrations, getCurrentBatch, insertMigration
57
+ }