2021-08-21 18:40:44 +03:00
|
|
|
import * as mysql from "mysql";
|
2021-08-21 17:50:54 +03:00
|
|
|
import { NodeConnection } from "../helper/config";
|
|
|
|
import { uuid } from "../helper/mainHelper";
|
2021-08-21 20:46:43 +03:00
|
|
|
import * as child from 'child_process';
|
|
|
|
import * as path from "path";
|
|
|
|
import * as fs from "fs";
|
2021-08-21 17:50:54 +03:00
|
|
|
|
2021-08-21 20:46:43 +03:00
|
|
|
export enum databaseExportLogType {
|
|
|
|
ERROR = "Error",
|
|
|
|
SUCCESS = "Success",
|
|
|
|
}
|
|
|
|
|
|
|
|
export interface databaseExportLog {
|
|
|
|
data: any,
|
|
|
|
type: databaseExportLogType,
|
|
|
|
error?: Error,
|
|
|
|
}
|
2021-08-21 17:50:54 +03:00
|
|
|
|
|
|
|
export class DataWorker {
|
|
|
|
|
|
|
|
/**
|
|
|
|
* An array of tasks that should be taken care of
|
|
|
|
*/
|
|
|
|
private nodeTasks : Array<NodeConnection> = [];
|
|
|
|
|
|
|
|
/**
|
|
|
|
* The amount of parallel tasks to run at once
|
|
|
|
*/
|
|
|
|
private parallelTasksCount: number;
|
|
|
|
|
2021-08-21 20:46:43 +03:00
|
|
|
private exportPath: string;
|
|
|
|
|
2021-08-21 17:50:54 +03:00
|
|
|
private id: string;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @param parallelTasks The amount of parallel tasks that this Worker can run at once
|
|
|
|
*/
|
2021-08-21 20:46:43 +03:00
|
|
|
constructor(parallelTasks: number, exportPath: string) {
|
2021-08-21 17:50:54 +03:00
|
|
|
// Initialize the amount of paralel tasks to run at the same time
|
|
|
|
this.parallelTasksCount = parallelTasks;
|
|
|
|
|
2021-08-21 20:46:43 +03:00
|
|
|
// Assign the export path
|
|
|
|
this.exportPath = exportPath;
|
|
|
|
|
2021-08-21 17:50:54 +03:00
|
|
|
// Generate an ID for the worker
|
|
|
|
this.id = uuid();
|
|
|
|
}
|
|
|
|
|
2021-08-21 18:40:44 +03:00
|
|
|
public getID() : string {
|
|
|
|
return this.id;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2021-08-21 17:50:54 +03:00
|
|
|
public addNodeTask(node: NodeConnection) {
|
|
|
|
// Add the task to the list
|
|
|
|
this.nodeTasks.push(node);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Start processing the node tasks.
|
|
|
|
*/
|
2021-08-21 20:46:43 +03:00
|
|
|
public startProcessing() : Promise<PromiseSettledResult<any>[] | string> {
|
2021-08-21 18:40:44 +03:00
|
|
|
return new Promise( async (_r, _e) => {
|
|
|
|
try {
|
2021-08-21 20:46:43 +03:00
|
|
|
|
|
|
|
if ( this.nodeTasks.length <= 0 ) {
|
|
|
|
return _r("No tasks to run, skipping");
|
|
|
|
}
|
|
|
|
|
2021-08-21 18:40:44 +03:00
|
|
|
// Go through all of the tasks to run
|
|
|
|
for ( let i = 0; i < this.nodeTasks.length; i++ ) {
|
|
|
|
// Spawn in the task queue
|
|
|
|
let taskQueue : Array<Promise<any>> = [];
|
|
|
|
|
|
|
|
// Start running the parallel tasks
|
|
|
|
for ( let x = 0; x < this.parallelTasksCount; x++ ) {
|
|
|
|
// Check if we have stept out of bounds
|
|
|
|
if ( i >= this.nodeTasks.length ) // Ignore out-of-bounds
|
|
|
|
break;
|
|
|
|
|
|
|
|
// Add the process to the queue
|
|
|
|
taskQueue.push(this.processTask(this.nodeTasks[i]))
|
|
|
|
// Add to the index
|
|
|
|
i++;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Return the resulting resolves
|
|
|
|
_r(await Promise.allSettled(taskQueue));
|
|
|
|
}
|
|
|
|
} catch ( ex ) {
|
|
|
|
_e(ex);
|
2021-08-21 17:50:54 +03:00
|
|
|
}
|
2021-08-21 18:40:44 +03:00
|
|
|
})
|
2021-08-21 17:50:54 +03:00
|
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Process a task
|
|
|
|
* @param task The task to process
|
|
|
|
*/
|
2021-08-21 18:40:44 +03:00
|
|
|
private processTask(task: NodeConnection) {
|
2021-08-21 20:46:43 +03:00
|
|
|
return new Promise<databaseExportLog[]>( async (_r, _e) => {
|
2021-08-21 18:40:44 +03:00
|
|
|
// Connect to the mysql database to test out the connection
|
|
|
|
const conn : mysql.Connection = mysql.createConnection({
|
|
|
|
host: task.hostname,
|
|
|
|
port: task.port,
|
|
|
|
user: task.username,
|
|
|
|
password: task.password,
|
|
|
|
});
|
|
|
|
|
|
|
|
try { // Try and connect to the database
|
|
|
|
// Connect to the database
|
|
|
|
await new Promise<void>( (rez, err) => {
|
|
|
|
conn.connect(e => {
|
|
|
|
if ( !!e ) {
|
|
|
|
// Mark the results as failure
|
|
|
|
err(e);
|
|
|
|
}
|
|
|
|
// Mark the promise as done
|
|
|
|
rez();
|
|
|
|
})
|
|
|
|
})
|
|
|
|
} catch ( error ) { // Return the connection error upstream
|
|
|
|
return _e(error);
|
|
|
|
}
|
|
|
|
|
2021-08-21 20:46:43 +03:00
|
|
|
let results: databaseExportLog[] = [];
|
|
|
|
|
2021-08-21 18:49:55 +03:00
|
|
|
try {
|
|
|
|
// Dump the database
|
2021-08-21 20:46:43 +03:00
|
|
|
results = await this.dumpDatabase(conn, task);
|
2021-08-21 18:49:55 +03:00
|
|
|
} catch ( err ) {
|
|
|
|
conn.destroy();
|
|
|
|
return _e(err);
|
|
|
|
}
|
2021-08-21 18:40:44 +03:00
|
|
|
|
|
|
|
try { // Close the connection
|
|
|
|
await new Promise<void>((re, er) => {
|
|
|
|
conn.end( err => {
|
|
|
|
// Just ignore it, whatever.
|
|
|
|
re();
|
|
|
|
});
|
|
|
|
})
|
|
|
|
} catch ( err ) {};
|
|
|
|
|
|
|
|
// Stop the code and mark as success
|
2021-08-21 20:46:43 +03:00
|
|
|
_r(results);
|
2021-08-21 18:40:44 +03:00
|
|
|
})
|
2021-08-21 17:50:54 +03:00
|
|
|
}
|
|
|
|
|
2021-08-21 18:49:55 +03:00
|
|
|
private dumpDatabase(conn : mysql.Connection, task: NodeConnection) {
|
2021-08-21 20:46:43 +03:00
|
|
|
return new Promise<databaseExportLog[]>( async (_r, _e) => {
|
2021-08-21 18:49:55 +03:00
|
|
|
|
|
|
|
// Get all of the databases from the connection
|
2021-08-21 20:46:43 +03:00
|
|
|
const databaseList: string[] = [];
|
|
|
|
let rawDatabaseList: string[] = [];
|
2021-08-21 18:49:55 +03:00
|
|
|
|
2021-08-21 20:46:43 +03:00
|
|
|
try {
|
|
|
|
rawDatabaseList = await new Promise<string[]>((res, er) => {
|
|
|
|
conn.query("SHOW DATABASES", (err, rez, fields) => {
|
|
|
|
if ( err ) {
|
|
|
|
// Could not list databases, there's definitely something wrong going to happen, do a forceful stop.
|
|
|
|
return _e(err);
|
|
|
|
}
|
|
|
|
|
|
|
|
let databaseList: string[] = [];
|
|
|
|
|
|
|
|
for ( let db of rez ) {
|
|
|
|
databaseList.push(db['Database']);
|
|
|
|
}
|
|
|
|
|
|
|
|
res(databaseList);
|
|
|
|
})
|
|
|
|
|
|
|
|
});
|
|
|
|
} catch ( ex ) {
|
|
|
|
return _e(ex);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Filter the database list based on the blacklist/whitelist
|
|
|
|
for ( let db of rawDatabaseList ) {
|
|
|
|
if ( !!task.databases ) {
|
|
|
|
|
|
|
|
// Check the whitelist
|
|
|
|
if ( !!task.databases.whitelist ) {
|
|
|
|
if ( !task.databases.whitelist?.includes(db) ) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Check the blacklist
|
|
|
|
if ( !!task.databases.blacklist ) {
|
|
|
|
if ( task.databases.blacklist?.includes(db) ) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// The database passed our filters, add it to the final list!
|
|
|
|
databaseList.push(db);
|
|
|
|
}
|
|
|
|
|
|
|
|
// List of logs for the different database dumps
|
|
|
|
const resultLogs: databaseExportLog[] = [];
|
|
|
|
|
|
|
|
// Run the mysqldump for every database
|
|
|
|
for ( let db of databaseList ) {
|
|
|
|
let startTime = new Date().getTime();
|
|
|
|
try {
|
|
|
|
await new Promise<void>((res, err) => {
|
|
|
|
// Get the path to export to
|
|
|
|
const exportPath: string = path.join( this.exportPath , `${db}.sql`);
|
|
|
|
// Make sure the folder exists
|
|
|
|
if ( !fs.existsSync(path.dirname(exportPath)) ) {
|
|
|
|
fs.mkdirSync(path.dirname(exportPath));
|
|
|
|
}
|
|
|
|
|
|
|
|
// Generate the export command
|
|
|
|
const exportCommand: string = `mysqldump -u${task.username} -p${task.password} -h${task.hostname} -P${task.port} ${db} > ${exportPath}`;
|
|
|
|
// Execute the export process
|
|
|
|
child.exec(exportCommand, (execErr, stdout, stderr) => {
|
|
|
|
if ( execErr ) {
|
|
|
|
|
|
|
|
resultLogs.push({
|
|
|
|
type: databaseExportLogType.ERROR,
|
|
|
|
data: {
|
|
|
|
runTime: (new Date().getTime() - startTime) + "ms",
|
|
|
|
stderr: stderr
|
|
|
|
},
|
|
|
|
error: execErr,
|
|
|
|
});
|
|
|
|
|
|
|
|
return res();
|
|
|
|
}
|
|
|
|
|
|
|
|
resultLogs.push({
|
|
|
|
type: databaseExportLogType.SUCCESS,
|
|
|
|
data: {
|
|
|
|
runTime: (new Date().getTime() - startTime) + "ms",
|
|
|
|
stdout: stdout,
|
|
|
|
},
|
|
|
|
});
|
|
|
|
|
|
|
|
res();
|
|
|
|
});
|
|
|
|
|
|
|
|
});
|
|
|
|
} catch ( error ) {
|
|
|
|
resultLogs.push({
|
|
|
|
type: databaseExportLogType.ERROR,
|
|
|
|
data: {
|
|
|
|
runTime: (new Date().getTime() - startTime) + "ms",
|
|
|
|
},
|
|
|
|
error: error
|
|
|
|
});
|
|
|
|
}
|
|
|
|
}
|
2021-08-21 18:49:55 +03:00
|
|
|
|
2021-08-21 20:46:43 +03:00
|
|
|
// Resolve the promise
|
|
|
|
_r(resultLogs);
|
2021-08-21 18:49:55 +03:00
|
|
|
})
|
|
|
|
}
|
|
|
|
|
2021-08-21 17:50:54 +03:00
|
|
|
}
|