Because cats have 9 lives, but servers don't - so they need backup-restore! Complete backup solution with S3/MinIO support. - Full WordPress backup (files + database) - S3 / MinIO / S3-compatible storage backends - Scheduled automatic backups - Disaster recovery / one-click restore - Backup integrity validation - Cat-themed admin interface Includes build.sh and .distignore for WordPress-installable release ZIPs.
382 lines
11 KiB
JavaScript
382 lines
11 KiB
JavaScript
#!/usr/bin/env node
|
|
|
|
/**
|
|
* Copy Astro build assets to WordPress plugin structure
|
|
* and generate PHP-readable manifest
|
|
*/
|
|
|
|
import fs from 'fs';
|
|
import path from 'path';
|
|
import { fileURLToPath } from 'url';
|
|
|
|
const __filename = fileURLToPath(import.meta.url);
|
|
const __dirname = path.dirname(__filename);
|
|
|
|
class WordPressAssetCopier {
|
|
constructor() {
|
|
this.projectRoot = path.resolve(__dirname, '..');
|
|
this.astroDistDir = path.join(this.projectRoot, 'admin/assets/dist');
|
|
this.wpAdminDir = path.join(this.projectRoot, 'admin');
|
|
|
|
this.manifest = {};
|
|
this.stats = {
|
|
filesCopied: 0,
|
|
errors: 0
|
|
};
|
|
}
|
|
|
|
async run() {
|
|
console.log('🚀 Starting WordPress asset integration...');
|
|
|
|
try {
|
|
// Ensure directories exist
|
|
await this.ensureDirectories();
|
|
|
|
// Parse Astro build output
|
|
await this.parseAstroOutput();
|
|
|
|
// Generate WordPress manifest
|
|
await this.generateManifest();
|
|
|
|
// Copy additional assets
|
|
await this.copyStaticAssets();
|
|
|
|
// Generate asset loader
|
|
await this.generateAssetLoader();
|
|
|
|
console.log(`✅ Successfully processed ${this.stats.filesCopied} files`);
|
|
if (this.stats.errors > 0) {
|
|
console.warn(`⚠️ ${this.stats.errors} errors encountered`);
|
|
}
|
|
|
|
} catch (error) {
|
|
console.error('❌ Asset integration failed:', error.message);
|
|
process.exit(1);
|
|
}
|
|
}
|
|
|
|
async ensureDirectories() {
|
|
const dirs = [
|
|
path.join(this.wpAdminDir, 'js'),
|
|
path.join(this.wpAdminDir, 'css'),
|
|
path.join(this.wpAdminDir, 'images')
|
|
];
|
|
|
|
for (const dir of dirs) {
|
|
if (!fs.existsSync(dir)) {
|
|
fs.mkdirSync(dir, { recursive: true });
|
|
}
|
|
}
|
|
}
|
|
|
|
async parseAstroOutput() {
|
|
if (!fs.existsSync(this.astroDistDir)) {
|
|
throw new Error(`Astro dist directory not found: ${this.astroDistDir}`);
|
|
}
|
|
|
|
// Read all files from dist directory
|
|
const files = await this.getAllFiles(this.astroDistDir);
|
|
|
|
for (const file of files) {
|
|
const relativePath = path.relative(this.astroDistDir, file);
|
|
const ext = path.extname(file).toLowerCase();
|
|
|
|
// Categorize and process files
|
|
if (ext === '.html') {
|
|
await this.processHtmlFile(file, relativePath);
|
|
} else if (ext === '.js') {
|
|
await this.processJsFile(file, relativePath);
|
|
} else if (ext === '.css') {
|
|
await this.processCssFile(file, relativePath);
|
|
} else if (['.png', '.jpg', '.jpeg', '.svg', '.gif'].includes(ext)) {
|
|
await this.processImageFile(file, relativePath);
|
|
}
|
|
}
|
|
}
|
|
|
|
async processHtmlFile(file, relativePath) {
|
|
const content = fs.readFileSync(file, 'utf-8');
|
|
const pageName = path.basename(relativePath, '.html');
|
|
|
|
// Extract CSS and JS references
|
|
const cssMatches = content.match(/<link[^>]*href="([^"]*\.css)"[^>]*>/g) || [];
|
|
const jsMatches = content.match(/<script[^>]*src="([^"]*\.js)"[^>]*>/g) || [];
|
|
|
|
const assets = {
|
|
css: cssMatches.map(match => {
|
|
const href = match.match(/href="([^"]*)"/)[1];
|
|
return href.startsWith('./') ? href.substring(2) : href;
|
|
}),
|
|
js: jsMatches.map(match => {
|
|
const src = match.match(/src="([^"]*)"/)[1];
|
|
return src.startsWith('./') ? src.substring(2) : src;
|
|
}),
|
|
html: relativePath
|
|
};
|
|
|
|
this.manifest[pageName] = assets;
|
|
|
|
// Copy HTML file to admin templates
|
|
const templateDir = path.join(this.wpAdminDir, 'templates');
|
|
if (!fs.existsSync(templateDir)) {
|
|
fs.mkdirSync(templateDir, { recursive: true });
|
|
}
|
|
|
|
const destPath = path.join(templateDir, `${pageName}.html`);
|
|
fs.copyFileSync(file, destPath);
|
|
|
|
this.stats.filesCopied++;
|
|
}
|
|
|
|
async processJsFile(file, relativePath) {
|
|
const destPath = path.join(this.wpAdminDir, relativePath);
|
|
const destDir = path.dirname(destPath);
|
|
|
|
if (!fs.existsSync(destDir)) {
|
|
fs.mkdirSync(destDir, { recursive: true });
|
|
}
|
|
|
|
// Process JavaScript for WordPress compatibility
|
|
let content = fs.readFileSync(file, 'utf-8');
|
|
|
|
// Replace placeholder variables with WordPress equivalents
|
|
content = content.replace(/__WP_NONCE__/g, 'window.tigerStyleLife9.nonce');
|
|
content = content.replace(/__WP_AJAX_URL__/g, 'window.tigerStyleLife9.ajaxUrl');
|
|
content = content.replace(/__WP_REST_URL__/g, 'window.tigerStyleLife9.restUrl');
|
|
content = content.replace(/__PLUGIN_URL__/g, 'window.tigerStyleLife9.pluginUrl');
|
|
|
|
fs.writeFileSync(destPath, content);
|
|
this.stats.filesCopied++;
|
|
}
|
|
|
|
async processCssFile(file, relativePath) {
|
|
const destPath = path.join(this.wpAdminDir, relativePath);
|
|
const destDir = path.dirname(destPath);
|
|
|
|
if (!fs.existsSync(destDir)) {
|
|
fs.mkdirSync(destDir, { recursive: true });
|
|
}
|
|
|
|
// Process CSS for WordPress admin compatibility
|
|
let content = fs.readFileSync(file, 'utf-8');
|
|
|
|
// Ensure all styles are scoped to plugin container
|
|
if (!content.includes('.tigerstyle-life9-container')) {
|
|
console.warn(`CSS file ${relativePath} may not be properly scoped`);
|
|
}
|
|
|
|
fs.writeFileSync(destPath, content);
|
|
this.stats.filesCopied++;
|
|
}
|
|
|
|
async processImageFile(file, relativePath) {
|
|
const destPath = path.join(this.wpAdminDir, relativePath);
|
|
const destDir = path.dirname(destPath);
|
|
|
|
if (!fs.existsSync(destDir)) {
|
|
fs.mkdirSync(destDir, { recursive: true });
|
|
}
|
|
|
|
fs.copyFileSync(file, destPath);
|
|
this.stats.filesCopied++;
|
|
}
|
|
|
|
async generateManifest() {
|
|
// Generate PHP manifest file
|
|
const phpManifest = this.generatePhpManifest();
|
|
const manifestPath = path.join(this.wpAdminDir, 'assets-manifest.php');
|
|
fs.writeFileSync(manifestPath, phpManifest);
|
|
|
|
// Generate JSON manifest for development
|
|
const jsonManifest = JSON.stringify(this.manifest, null, 2);
|
|
const jsonPath = path.join(this.wpAdminDir, 'assets-manifest.json');
|
|
fs.writeFileSync(jsonPath, jsonManifest);
|
|
|
|
console.log(`📝 Generated manifest with ${Object.keys(this.manifest).length} pages`);
|
|
}
|
|
|
|
generatePhpManifest() {
|
|
return `<?php
|
|
/**
|
|
* Auto-generated asset manifest
|
|
* Generated: ${new Date().toISOString()}
|
|
* Do not edit manually
|
|
*/
|
|
|
|
// Exit if accessed directly
|
|
if (!defined('ABSPATH')) {
|
|
exit;
|
|
}
|
|
|
|
return ${this.phpStringify(this.manifest, 0)};`;
|
|
}
|
|
|
|
phpStringify(obj, indent = 0) {
|
|
const spaces = ' '.repeat(indent);
|
|
|
|
if (Array.isArray(obj)) {
|
|
if (obj.length === 0) return '[]';
|
|
|
|
const items = obj.map(item =>
|
|
`${spaces} ${this.phpStringify(item, indent + 1)}`
|
|
).join(',\n');
|
|
|
|
return `[\n${items}\n${spaces}]`;
|
|
}
|
|
|
|
if (typeof obj === 'object' && obj !== null) {
|
|
const keys = Object.keys(obj);
|
|
if (keys.length === 0) return '[]';
|
|
|
|
const items = keys.map(key =>
|
|
`${spaces} '${key}' => ${this.phpStringify(obj[key], indent + 1)}`
|
|
).join(',\n');
|
|
|
|
return `[\n${items}\n${spaces}]`;
|
|
}
|
|
|
|
if (typeof obj === 'string') {
|
|
return `'${obj.replace(/'/g, "\\'")}'`;
|
|
}
|
|
|
|
if (typeof obj === 'boolean') {
|
|
return obj ? 'true' : 'false';
|
|
}
|
|
|
|
if (typeof obj === 'number') {
|
|
return obj.toString();
|
|
}
|
|
|
|
return 'null';
|
|
}
|
|
|
|
async copyStaticAssets() {
|
|
const publicDir = path.join(this.projectRoot, 'src/astro/public');
|
|
|
|
if (fs.existsSync(publicDir)) {
|
|
const files = await this.getAllFiles(publicDir);
|
|
|
|
for (const file of files) {
|
|
const relativePath = path.relative(publicDir, file);
|
|
const destPath = path.join(this.wpAdminDir, 'static', relativePath);
|
|
const destDir = path.dirname(destPath);
|
|
|
|
if (!fs.existsSync(destDir)) {
|
|
fs.mkdirSync(destDir, { recursive: true });
|
|
}
|
|
|
|
fs.copyFileSync(file, destPath);
|
|
this.stats.filesCopied++;
|
|
}
|
|
}
|
|
}
|
|
|
|
async generateAssetLoader() {
|
|
const loaderContent = `<?php
|
|
/**
|
|
* WordPress Asset Loader for TigerStyle Life9
|
|
* Auto-generated asset loading functions
|
|
*/
|
|
|
|
// Exit if accessed directly
|
|
if (!defined('ABSPATH')) {
|
|
exit;
|
|
}
|
|
|
|
/**
|
|
* Load assets for a specific page
|
|
*
|
|
* @param string $page_name Page name (e.g., 'backup-interface')
|
|
* @param array $extra_data Extra data to localize
|
|
*/
|
|
function tigerstyle_life9_load_page_assets($page_name, $extra_data = []) {
|
|
$manifest = require_once TIGERSTYLE_LIFE9_PLUGIN_DIR . 'admin/assets-manifest.php';
|
|
|
|
if (!isset($manifest[$page_name])) {
|
|
return false;
|
|
}
|
|
|
|
$assets = $manifest[$page_name];
|
|
$plugin_url = TIGERSTYLE_LIFE9_PLUGIN_URL;
|
|
|
|
// Enqueue CSS files
|
|
if (!empty($assets['css'])) {
|
|
foreach ($assets['css'] as $index => $css_file) {
|
|
wp_enqueue_style(
|
|
"tigerstyle-life9-{$page_name}-{$index}",
|
|
$plugin_url . 'admin/' . $css_file,
|
|
[],
|
|
TIGERSTYLE_LIFE9_VERSION
|
|
);
|
|
}
|
|
}
|
|
|
|
// Enqueue JavaScript files
|
|
if (!empty($assets['js'])) {
|
|
foreach ($assets['js'] as $index => $js_file) {
|
|
$handle = "tigerstyle-life9-{$page_name}-{$index}";
|
|
|
|
wp_enqueue_script(
|
|
$handle,
|
|
$plugin_url . 'admin/' . $js_file,
|
|
['jquery', 'wp-api'],
|
|
TIGERSTYLE_LIFE9_VERSION,
|
|
true
|
|
);
|
|
|
|
// Localize script data on the first JS file
|
|
if ($index === 0) {
|
|
$localize_data = array_merge([
|
|
'nonce' => wp_create_nonce('tigerstyle_life9_nonce'),
|
|
'ajaxUrl' => admin_url('admin-ajax.php'),
|
|
'restUrl' => rest_url('tigerstyle-life9/v1/'),
|
|
'pluginUrl' => $plugin_url,
|
|
'currentUser' => get_current_user_id(),
|
|
'capabilities' => [
|
|
'manage_backups' => current_user_can('manage_options'),
|
|
'download_backups' => current_user_can('manage_options')
|
|
],
|
|
'strings' => [
|
|
'backupStarted' => __('Backup Started', 'tigerstyle-life9'),
|
|
'backupComplete' => __('Backup Complete', 'tigerstyle-life9'),
|
|
'error' => __('Error', 'tigerstyle-life9'),
|
|
'confirm' => __('Are you sure?', 'tigerstyle-life9')
|
|
]
|
|
], $extra_data);
|
|
|
|
wp_localize_script($handle, 'tigerStyleLife9', $localize_data);
|
|
}
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}`;
|
|
|
|
const loaderPath = path.join(this.wpAdminDir, 'asset-loader.php');
|
|
fs.writeFileSync(loaderPath, loaderContent);
|
|
|
|
console.log('🔧 Generated WordPress asset loader');
|
|
}
|
|
|
|
async getAllFiles(dir) {
|
|
const files = [];
|
|
|
|
const entries = fs.readdirSync(dir, { withFileTypes: true });
|
|
|
|
for (const entry of entries) {
|
|
const fullPath = path.join(dir, entry.name);
|
|
|
|
if (entry.isDirectory()) {
|
|
files.push(...await this.getAllFiles(fullPath));
|
|
} else {
|
|
files.push(fullPath);
|
|
}
|
|
}
|
|
|
|
return files;
|
|
}
|
|
}
|
|
|
|
// Run the copier
|
|
const copier = new WordPressAssetCopier();
|
|
copier.run().catch(console.error); |