UNPKG

wp-host

Version:

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

508 lines 19.3 kB
"use strict"; var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { if (k2 === undefined) k2 = k; var desc = Object.getOwnPropertyDescriptor(m, k); if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { desc = { enumerable: true, get: function() { return m[k]; } }; } Object.defineProperty(o, k2, desc); }) : (function(o, m, k, k2) { if (k2 === undefined) k2 = k; o[k2] = m[k]; })); var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { Object.defineProperty(o, "default", { enumerable: true, value: v }); }) : function(o, v) { o["default"] = v; }); var __importStar = (this && this.__importStar) || (function () { var ownKeys = function(o) { ownKeys = Object.getOwnPropertyNames || function (o) { var ar = []; for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k; return ar; }; return ownKeys(o); }; return function (mod) { if (mod && mod.__esModule) return mod; var result = {}; if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]); __setModuleDefault(result, mod); return result; }; })(); Object.defineProperty(exports, "__esModule", { value: true }); exports.ConfigParser = void 0; const fs = __importStar(require("fs-extra")); const path = __importStar(require("path")); class ConfigParser { /** * Set whether to use _db suffix for database names */ static setNoDbSuffix(value) { this.noDbSuffix = value; } /** * Parse configuration file (JSON or CSV) */ static async parseConfig(configPath) { const ext = path.extname(configPath).toLowerCase(); if (ext === ".json") { return this.parseJsonConfig(configPath); } else if (ext === ".csv") { return this.parseCsvConfig(configPath); } else { throw new Error(`Unsupported configuration file format: ${ext}. Supported formats: .json, .csv`); } } /** * Parse JSON configuration file */ static async parseJsonConfig(configPath) { try { const content = await fs.readFile(configPath, "utf-8"); const config = JSON.parse(content); // Auto-generate missing database names and users this.autoGenerateDbInfo(config); this.validateConfig(config); return config; } catch (error) { if (error instanceof SyntaxError) { throw new Error(`Invalid JSON in configuration file: ${error.message}`); } throw error; } } /** * Parse CSV configuration file */ static async parseCsvConfig(configPath) { try { const content = await fs.readFile(configPath, "utf-8"); const lines = content.trim().split("\n"); if (lines.length < 2) { throw new Error("CSV file must have at least a header row and one data row"); } const headers = lines[0].split(",").map((h) => h.trim()); // Check if this is the comprehensive format (includes MySQL and WordPress config) const isComprehensiveFormat = headers.some((h) => h.toLowerCase() === "mysql host" || h === "mysql_host") && headers.some((h) => h.toLowerCase() === "wordpress admin password" || h === "wordpress_admin_password"); if (isComprehensiveFormat) { return this.parseComprehensiveCSV(lines, headers); } else { return this.parseSimpleCSV(lines, headers); } } catch (error) { throw new Error(`Error parsing CSV file: ${error instanceof Error ? error.message : String(error)}`); } } /** * Parse comprehensive CSV format with all configuration included */ static parseComprehensiveCSV(lines, headers) { // Create header mapping for both technical and user-friendly headers const headerMap = this.createHeaderMap(headers); const requiredFields = [ "site_name", "directory_path", "wordpress_site_title", "wordpress_admin_username", "mysql_host", "mysql_port", "mysql_root_user", "mysql_root_password", "mysql_shared_db_password", "wordpress_admin_password", "wordpress_admin_email", ]; // Check for required headers (using mapped names) for (const required of requiredFields) { if (!headerMap[required]) { throw new Error(`Missing required CSV header: ${required} (or its user-friendly equivalent)`); } } const sites = []; let mysqlConfig = null; let wordpressConfig = null; for (let i = 1; i < lines.length; i++) { const values = lines[i].split(",").map((v) => v.trim()); if (values.length !== headers.length) { throw new Error(`Row ${i + 1}: Expected ${headers.length} columns, got ${values.length}`); } const row = {}; headers.forEach((header, index) => { row[header] = values[index]; }); // Map headers to standardized field names const mappedRow = this.mapRowData(row, headerMap); // Extract MySQL config from first row (assuming all rows have same config) if (!mysqlConfig) { mysqlConfig = { host: mappedRow.mysql_host, port: parseInt(mappedRow.mysql_port), rootUser: mappedRow.mysql_root_user, rootPassword: mappedRow.mysql_root_password, sharedDbPassword: mappedRow.mysql_shared_db_password, }; } // Extract WordPress config from first row (shared settings) if (!wordpressConfig) { wordpressConfig = { adminPassword: mappedRow.wordpress_admin_password, adminEmail: mappedRow.wordpress_admin_email, adminUsername: "admin", // Default, will be overridden per site siteTitle: "WordPress Site", // Default, will be overridden per site }; } // Create site config with per-site WordPress settings const site = { site_name: mappedRow.site_name, directory_path: mappedRow.directory_path, database_name: this.noDbSuffix ? mappedRow.site_name : `${mappedRow.site_name}_db`, db_user: `${mappedRow.site_name}_user`, wordpress_site_title: mappedRow.wordpress_site_title, wordpress_admin_username: mappedRow.wordpress_admin_username, }; sites.push(site); } const config = { mysql: mysqlConfig, wordpress: wordpressConfig, sites, }; this.validateConfig(config); return config; } /** * Parse simple CSV format (legacy support - requires environment variables) */ static parseSimpleCSV(lines, headers) { const requiredHeaders = ["site_name", "directory_path"]; // Validate headers for (const required of requiredHeaders) { if (!headers.includes(required)) { throw new Error(`Missing required CSV header: ${required}`); } } const sites = []; for (let i = 1; i < lines.length; i++) { const values = lines[i].split(",").map((v) => v.trim()); if (values.length !== headers.length) { throw new Error(`Row ${i + 1}: Expected ${headers.length} columns, got ${values.length}`); } const site = { site_name: "", directory_path: "", }; headers.forEach((header, index) => { const value = values[index]; switch (header) { case "site_name": site.site_name = value; break; case "directory_path": site.directory_path = value; break; case "database_name": site.database_name = value; break; case "db_user": site.db_user = value; break; } }); sites.push(site); } // For simple CSV, use default or environment-based config const config = { mysql: this.getDefaultMySQLConfig(), wordpress: this.getDefaultWordPressConfig(), sites, }; // Auto-generate missing database names and users this.autoGenerateDbInfo(config); this.validateConfig(config); return config; } /** * Auto-generate database names and users if not provided */ static autoGenerateDbInfo(config) { config.sites.forEach((site) => { // Auto-generate database name if not provided if (!site.database_name) { site.database_name = this.noDbSuffix ? site.site_name : `${site.site_name}_db`; } // Auto-generate database user if not provided if (!site.db_user) { site.db_user = `${site.site_name}_user`; } }); } /** * Create header mapping for both technical and user-friendly headers */ static createHeaderMap(headers) { const headerMap = {}; // Header mapping from user-friendly to technical names const mappings = { "Site Name": "site_name", site_name: "site_name", "Directory Path": "directory_path", directory_path: "directory_path", "MySQL Host": "mysql_host", mysql_host: "mysql_host", "MySQL Port": "mysql_port", mysql_port: "mysql_port", "MySQL Username": "mysql_root_user", mysql_root_user: "mysql_root_user", "MySQL Password": "mysql_root_password", mysql_root_password: "mysql_root_password", "Database Password": "mysql_shared_db_password", mysql_shared_db_password: "mysql_shared_db_password", "WordPress Site Title": "wordpress_site_title", wordpress_site_title: "wordpress_site_title", "WordPress Admin Username": "wordpress_admin_username", wordpress_admin_username: "wordpress_admin_username", "WordPress Admin Password": "wordpress_admin_password", wordpress_admin_password: "wordpress_admin_password", "WordPress Admin Email": "wordpress_admin_email", wordpress_admin_email: "wordpress_admin_email", }; // Create reverse mapping from technical names to actual headers headers.forEach((header) => { const technicalName = mappings[header]; if (technicalName) { headerMap[technicalName] = header; } }); return headerMap; } /** * Map row data using header mapping */ static mapRowData(row, headerMap) { const mappedRow = {}; Object.keys(headerMap).forEach((technicalName) => { const actualHeader = headerMap[technicalName]; mappedRow[technicalName] = row[actualHeader]; }); return mappedRow; } /** * Validate configuration structure and data */ static validateConfig(config) { const errors = []; // Validate MySQL config is provided if (!config.mysql) { errors.push({ field: "mysql", message: "MySQL configuration is required", }); } else { this.validateMySQLConfig(config.mysql, errors); } // Validate WordPress config is provided if (!config.wordpress) { errors.push({ field: "wordpress", message: "WordPress configuration is required", }); } else { this.validateWordPressConfig(config.wordpress, errors); } // Validate sites array exists if (!config.sites || !Array.isArray(config.sites)) { errors.push({ field: "sites", message: 'Configuration must contain a "sites" array', }); throw new Error(`Configuration validation failed:\n${errors.map((e) => `- ${e.field}: ${e.message}`).join("\n")}`); } // Validate sites array is not empty if (config.sites.length === 0) { errors.push({ field: "sites", message: "Sites array cannot be empty", }); } // Validate each site configuration config.sites.forEach((site, index) => { this.validateSiteConfig(site, index, errors); }); // Check for duplicate site names and database names this.checkDuplicates(config.sites, errors); if (errors.length > 0) { throw new Error(`Configuration validation failed:\n${errors .map((e) => e.siteIndex !== undefined ? `- Site ${e.siteIndex + 1} (${e.field}): ${e.message}` : `- ${e.field}: ${e.message}`) .join("\n")}`); } } /** * Validate individual site configuration */ static validateSiteConfig(site, index, errors) { // Required fields if (!site.site_name || site.site_name.trim() === "") { errors.push({ field: "site_name", message: "Site name is required and cannot be empty", siteIndex: index, }); } if (!site.directory_path || site.directory_path.trim() === "") { errors.push({ field: "directory_path", message: "Directory path is required and cannot be empty", siteIndex: index, }); } // Validate site name format (alphanumeric, underscore, hyphen) if (site.site_name && !/^[a-zA-Z0-9_-]+$/.test(site.site_name)) { errors.push({ field: "site_name", message: "Site name can only contain letters, numbers, underscores, and hyphens", siteIndex: index, }); } // Validate database name format (MySQL naming rules) - auto-generated, so should be valid if (site.database_name && !/^[a-zA-Z0-9_]+$/.test(site.database_name)) { errors.push({ field: "database_name", message: "Database name can only contain letters, numbers, and underscores", siteIndex: index, }); } } /** * Validate MySQL configuration */ static validateMySQLConfig(mysql, errors) { if (!mysql.host || mysql.host.trim() === "") { errors.push({ field: "mysql.host", message: "MySQL host is required", }); } if (!mysql.port || mysql.port < 1 || mysql.port > 65535) { errors.push({ field: "mysql.port", message: "MySQL port must be between 1 and 65535", }); } if (!mysql.rootUser || mysql.rootUser.trim() === "") { errors.push({ field: "mysql.rootUser", message: "MySQL root user is required", }); } if (!mysql.rootPassword || mysql.rootPassword.trim() === "") { errors.push({ field: "mysql.rootPassword", message: "MySQL root password is required", }); } if (!mysql.sharedDbPassword || mysql.sharedDbPassword.trim() === "") { errors.push({ field: "mysql.sharedDbPassword", message: "Shared database password is required", }); } } /** * Validate WordPress configuration */ static validateWordPressConfig(wordpress, errors) { if (!wordpress.adminPassword || wordpress.adminPassword.trim() === "") { errors.push({ field: "wordpress.adminPassword", message: "WordPress admin password is required", }); } if (!wordpress.adminEmail || wordpress.adminEmail.trim() === "") { errors.push({ field: "wordpress.adminEmail", message: "WordPress admin email is required", }); } // Validate email format if (wordpress.adminEmail && !/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(wordpress.adminEmail)) { errors.push({ field: "wordpress.adminEmail", message: "Invalid email format", }); } } /** * Check for duplicate site names and database names */ static checkDuplicates(sites, errors) { const siteNames = new Set(); const dbNames = new Set(); sites.forEach((site, index) => { if (siteNames.has(site.site_name)) { errors.push({ field: "site_name", message: `Duplicate site name: ${site.site_name}`, siteIndex: index, }); } else { siteNames.add(site.site_name); } if (site.database_name && dbNames.has(site.database_name)) { errors.push({ field: "database_name", message: `Duplicate database name: ${site.database_name}`, siteIndex: index, }); } else if (site.database_name) { dbNames.add(site.database_name); } }); } /** * Get default MySQL configuration */ static getDefaultMySQLConfig() { return { host: "localhost", port: 3306, rootUser: "root", rootPassword: "your_mysql_root_password", sharedDbPassword: "shared_db_password_123", }; } /** * Get default WordPress configuration */ static getDefaultWordPressConfig() { return { adminPassword: "wp_admin_password_123", adminEmail: "admin@example.com", adminUsername: "admin", siteTitle: "My WordPress Site", }; } } exports.ConfigParser = ConfigParser; ConfigParser.noDbSuffix = false; //# sourceMappingURL=config-parser.js.map