wp-host
Version:
Automated WordPress hosting deployment tool for bulk site creation with MySQL database management
508 lines • 19.3 kB
JavaScript
;
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