UNPKG

wp-host

Version:

Automated WordPress hosting deployment tool for bulk site creation with MySQL database management

350 lines โ€ข 14.9 kB
"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.MySQLManager = void 0; const promise_1 = __importDefault(require("mysql2/promise")); class MySQLManager { constructor(config) { this.connection = null; this.config = config; } /** * Establish connection to MySQL server as root user */ async connect() { try { console.log(`๐Ÿ”Œ Connecting to MySQL at ${this.config.host}:${this.config.port}...`); this.connection = await promise_1.default.createConnection({ host: this.config.host, port: this.config.port, user: this.config.rootUser, password: this.config.rootPassword, // Don't specify a database - we're connecting as root to create databases }); // Test the connection await this.connection.ping(); console.log(`โœ… Successfully connected to MySQL as ${this.config.rootUser}`); } catch (error) { const errorMessage = error instanceof Error ? error.message : String(error); console.error(`โŒ Failed to connect to MySQL: ${errorMessage}`); // Provide helpful error messages for common issues if (errorMessage.includes("ECONNREFUSED")) { throw new Error(`Cannot connect to MySQL server at ${this.config.host}:${this.config.port}. Is MySQL running?`); } else if (errorMessage.includes("Access denied")) { throw new Error(`Access denied for user '${this.config.rootUser}'. Check your MySQL root password.`); } else if (errorMessage.includes("ENOTFOUND")) { throw new Error(`MySQL host '${this.config.host}' not found. Check your host configuration.`); } else { throw new Error(`MySQL connection failed: ${errorMessage}`); } } } /** * Test MySQL connection without storing it */ async testConnection() { let testConnection = null; try { console.log(`๐Ÿงช Testing MySQL connection...`); testConnection = await promise_1.default.createConnection({ host: this.config.host, port: this.config.port, user: this.config.rootUser, password: this.config.rootPassword, }); await testConnection.ping(); console.log(`โœ… MySQL connection test successful`); return true; } catch (error) { const errorMessage = error instanceof Error ? error.message : String(error); console.error(`โŒ MySQL connection test failed: ${errorMessage}`); return false; } finally { if (testConnection) { await testConnection.end(); } } } /** * Check if a database exists */ async databaseExists(databaseName) { if (!this.connection) { throw new Error("Not connected to MySQL. Call connect() first."); } try { const [rows] = await this.connection.execute("SELECT SCHEMA_NAME FROM INFORMATION_SCHEMA.SCHEMATA WHERE SCHEMA_NAME = ?", [databaseName]); return Array.isArray(rows) && rows.length > 0; } catch (error) { const errorMessage = error instanceof Error ? error.message : String(error); throw new Error(`Failed to check if database exists: ${errorMessage}`); } } /** * Check if a user exists */ async userExists(username, host = "localhost") { if (!this.connection) { throw new Error("Not connected to MySQL. Call connect() first."); } try { const [rows] = await this.connection.execute("SELECT User FROM mysql.user WHERE User = ? AND Host = ?", [username, host]); return Array.isArray(rows) && rows.length > 0; } catch (error) { const errorMessage = error instanceof Error ? error.message : String(error); throw new Error(`Failed to check if user exists: ${errorMessage}`); } } /** * Create a new database */ async createDatabase(databaseName) { if (!this.connection) { throw new Error("Not connected to MySQL. Call connect() first."); } try { console.log(`๐Ÿ“Š Creating database: ${databaseName}`); // Check if database already exists if (await this.databaseExists(databaseName)) { console.log(`โš ๏ธ Database ${databaseName} already exists, skipping creation`); return; } await this.connection.execute(`CREATE DATABASE \`${databaseName}\``); console.log(`โœ… Database ${databaseName} created successfully`); } catch (error) { const errorMessage = error instanceof Error ? error.message : String(error); throw new Error(`Failed to create database ${databaseName}: ${errorMessage}`); } } /** * Create a new MySQL user */ async createUser(username, password, host = "localhost") { if (!this.connection) { throw new Error("Not connected to MySQL. Call connect() first."); } try { console.log(`๐Ÿ‘ค Creating user: ${username}@${host}`); // Check if user already exists if (await this.userExists(username, host)) { console.log(`โš ๏ธ User ${username}@${host} already exists, skipping creation`); return; } // For MySQL 5.7 compatibility, use string concatenation instead of parameterized password const createUserSQL = `CREATE USER \`${username}\`@\`${host}\` IDENTIFIED BY '${password.replace(/'/g, "''")}'`; console.log(`๐Ÿ” Executing SQL: ${createUserSQL.replace(password, "[PASSWORD_HIDDEN]")}`); await this.connection.execute(createUserSQL); console.log(`โœ… User ${username}@${host} created successfully`); } catch (error) { const errorMessage = error instanceof Error ? error.message : String(error); throw new Error(`Failed to create user ${username}@${host}: ${errorMessage}`); } } /** * Grant privileges to a user for a specific database */ async grantPrivileges(username, databaseName, host = "localhost") { if (!this.connection) { throw new Error("Not connected to MySQL. Call connect() first."); } try { console.log(`๐Ÿ”‘ Granting privileges on ${databaseName} to ${username}@${host}`); await this.connection.execute(`GRANT ALL PRIVILEGES ON \`${databaseName}\`.* TO \`${username}\`@\`${host}\``); // Flush privileges to ensure they take effect immediately await this.connection.execute("FLUSH PRIVILEGES"); console.log(`โœ… Privileges granted successfully`); } catch (error) { const errorMessage = error instanceof Error ? error.message : String(error); throw new Error(`Failed to grant privileges: ${errorMessage}`); } } /** * Test connection with specific database credentials */ async testDatabaseConnection(username, password, databaseName, host = "localhost") { let testConnection = null; try { testConnection = await promise_1.default.createConnection({ host: this.config.host, port: this.config.port, user: username, password: password, database: databaseName, }); await testConnection.ping(); return true; } catch (error) { return false; } finally { if (testConnection) { await testConnection.end(); } } } /** * Get MySQL server version and info */ async getServerInfo() { if (!this.connection) { throw new Error("Not connected to MySQL. Call connect() first."); } try { const [rows] = await this.connection.execute("SELECT VERSION() as version"); const version = Array.isArray(rows) && rows.length > 0 ? rows[0].version : "Unknown"; return { version, host: this.config.host, port: this.config.port, }; } catch (error) { const errorMessage = error instanceof Error ? error.message : String(error); throw new Error(`Failed to get server info: ${errorMessage}`); } } /** * Close the MySQL connection */ async disconnect() { if (this.connection) { try { await this.connection.end(); this.connection = null; console.log(`๐Ÿ”Œ Disconnected from MySQL`); } catch (error) { const errorMessage = error instanceof Error ? error.message : String(error); console.error(`โš ๏ธ Error disconnecting from MySQL: ${errorMessage}`); } } } /** * Execute a raw SQL query (for advanced operations) */ async executeQuery(query, params) { if (!this.connection) { throw new Error("Not connected to MySQL. Call connect() first."); } try { const [rows] = await this.connection.execute(query, params); return rows; } catch (error) { const errorMessage = error instanceof Error ? error.message : String(error); throw new Error(`Failed to execute query: ${errorMessage}`); } } /** * Drop database and user completely for a clean slate */ async dropDatabaseAndUser(databaseName, username, host = "localhost") { if (!this.connection) { throw new Error("Not connected to MySQL. Call connect() first."); } try { // Drop database if it exists - using IF EXISTS to avoid errors await this.connection.execute(`DROP DATABASE IF EXISTS \`${databaseName}\``); // Drop user if it exists - using IF EXISTS to avoid errors await this.connection.execute(`DROP USER IF EXISTS \`${username}\`@\`${host}\``); // Flush privileges to ensure changes take effect await this.connection.execute("FLUSH PRIVILEGES"); } catch (error) { const errorMessage = error instanceof Error ? error.message : String(error); throw new Error(`Failed to perform clean slate reset: ${errorMessage}`); } } /** * Create database and user with full permissions (clean slate approach) */ async createDatabaseAndUserClean(databaseName, username, password, host = "localhost") { if (!this.connection) { throw new Error("Not connected to MySQL. Call connect() first."); } try { // First, ensure clean slate by dropping existing database and user await this.dropDatabaseAndUser(databaseName, username, host); // Create database await this.connection.execute(`CREATE DATABASE \`${databaseName}\``); // Create user const createUserSQL = `CREATE USER \`${username}\`@\`${host}\` IDENTIFIED BY '${password.replace(/'/g, "''")}'`; await this.connection.execute(createUserSQL); // Grant all privileges await this.connection.execute(`GRANT ALL PRIVILEGES ON \`${databaseName}\`.* TO \`${username}\`@\`${host}\``); // Flush privileges await this.connection.execute("FLUSH PRIVILEGES"); } catch (error) { const errorMessage = error instanceof Error ? error.message : String(error); throw new Error(`Failed to create database and user with clean slate: ${errorMessage}`); } } /** * Create or use existing database and ensure user has access */ async createOrUseDatabase(databaseName, username, password, host = "localhost") { if (!this.connection) { throw new Error("Not connected to MySQL. Call connect() first."); } let created = false; let userCreated = false; try { // Check if database exists const dbExists = await this.databaseExists(databaseName); if (!dbExists) { // Create database if it doesn't exist await this.connection.execute(`CREATE DATABASE \`${databaseName}\``); created = true; console.log(`โœ… Created new database: ${databaseName}`); } else { console.log(`โ„น๏ธ Using existing database: ${databaseName}`); } // Check if user exists const userExists = await this.userExists(username, host); if (!userExists) { // Create user if it doesn't exist const createUserSQL = `CREATE USER \`${username}\`@\`${host}\` IDENTIFIED BY '${password.replace(/'/g, "''")}'`; await this.connection.execute(createUserSQL); userCreated = true; console.log(`โœ… Created new user: ${username}@${host}`); } else { console.log(`โ„น๏ธ User already exists: ${username}@${host}`); // Update password for existing user const updatePasswordSQL = `ALTER USER \`${username}\`@\`${host}\` IDENTIFIED BY '${password.replace(/'/g, "''")}'`; await this.connection.execute(updatePasswordSQL); console.log(`โœ… Updated password for user: ${username}@${host}`); } // Grant privileges (in case they don't have them already) await this.connection.execute(`GRANT ALL PRIVILEGES ON \`${databaseName}\`.* TO \`${username}\`@\`${host}\``); // Flush privileges await this.connection.execute("FLUSH PRIVILEGES"); console.log(`โœ… Ensured privileges for ${username}@${host} on ${databaseName}`); return { created, userCreated }; } catch (error) { const errorMessage = error instanceof Error ? error.message : String(error); throw new Error(`Failed to create or use database: ${errorMessage}`); } } } exports.MySQLManager = MySQLManager; //# sourceMappingURL=mysql-manager.js.map